home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / gnu / bash / bash_108 / bash-108.zoo / bash-1.08 / builtins.c.orig < prev    next >
Encoding:
Text File  |  1991-05-20  |  119.8 KB  |  4,830 lines

  1. /* builtins.c -- the built in shell commands. */
  2.  
  3. /* Copyright (C) 1987,1991 Free Software Foundation, Inc.
  4.  
  5.    This file is part of GNU Bash, the Bourne Again SHell.
  6.  
  7.    Bash is free software; you can redistribute it and/or modify it
  8.    under the terms of the GNU General Public License as published by
  9.    the Free Software Foundation; either version 1, or (at your option)
  10.    any later version.
  11.  
  12.    Bash is distributed in the hope that it will be useful, but WITHOUT
  13.    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  14.    or FITNESS FOR A PARTICULAR PURPOSE.     See the GNU General Public
  15.    License for more details.
  16.  
  17.    You should have received a copy of the GNU General Public License
  18.    along with Bash; see the file COPYING.  If not, write to the Free
  19.    Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include <stdio.h>
  22. #include <sys/param.h>
  23.  
  24. #include <sys/types.h>
  25. #include "posixstat.h"
  26. #include <sys/file.h>
  27. #include <errno.h>
  28. #include "shell.h"
  29.  
  30. #if defined (HAVE_RESOURCE) && !defined (hpux)
  31. #include <sys/time.h>
  32. #include <sys/resource.h>
  33. #else /* !HAVE_RESOURCE || hpux */
  34. #include <sys/times.h>
  35. #endif /* !HAVE_RESOURCE || hpux */
  36.  
  37. #include "builtins.h"
  38. #include "trap.h"
  39. #include "flags.h"
  40. #include <readline/history.h>
  41. #include "jobs.h"
  42.  
  43. extern int errno;        /* Not always in <errno.h>.  Bogusness. */
  44.  
  45. #ifndef sigmask
  46. #define sigmask(x) (1 << ((x)-1))
  47. #endif
  48.  
  49. #if defined (USG)
  50. #include <fcntl.h>
  51. #endif
  52.  
  53. #if defined (HAVE_VPRINTF)
  54. #include <varargs.h>
  55. #endif
  56.  
  57. /* Yecch!  Who cares about this gross concept in the first place? */
  58. #ifndef MAXPATHLEN
  59. #define MAXPATHLEN 1024
  60. #endif
  61.  
  62. /* The command name of the currently running function. */
  63. extern char *this_command_name;
  64.  
  65. /* Non-zero means running an interactive shell. */
  66. extern int interactive;
  67.  
  68. /* The list of shell builtins.    Each element is name, function, enabled-p,
  69.    short-doc, long-doc.     The long-doc field should contain a set of indented
  70.    lines.  The function takes a WORD_LIST *, where the first word in the list
  71.    is the first arg to the command.  The list has already been word expanded.
  72.  
  73.    Procedures which need to look at every simple command (like enable_builtin),
  74.    should tree-walk looking for (array[i].function == (Function *)NULL).  The
  75.    list of executable builtins (in the shell sense) ends there.     Then comes
  76.    the control structure commands, like `if' and `while'.  */
  77.  
  78. struct builtin shell_builtins[] = {
  79.  
  80.   { ":", colon_builtin, 1, ":",
  81.       "    No effect; the command does nothing.  A zero exit code is returned" },
  82.  
  83.   { ".", period_builtin, 1, ". [filename]",
  84.       "    Read and execute commands from FILENAME and return.\n\
  85.     Pathnames in $PATH are used to find the directory containing FILENAME" },
  86.  
  87.   { "alias", alias_builtin, 1, "alias [ name[=value] ... ]",
  88.       "    Alias with no arguments prints the list of aliases in the form\n\
  89.     name=value on standard output.    An alias is defined for each NAME\n\
  90.     whose VALUE is given.  A trailing space in VALUE causes the next\n\
  91.     word to be checked for alias substitution.  Alias returns true\n\
  92.     unless a NAME is given for which no alias has been defined" },
  93.  
  94. #if defined (JOB_CONTROL)
  95.   { "bg", bg_builtin, 1, "bg [job_spec]",
  96.       "    Place JOB_SPEC in the background, as if it had been started with\n\
  97.     `&'.  If JOB_SPEC is not present, the shell's notion of the current\n\
  98.     job is used" },
  99. #endif /* JOB_CONTROL */
  100.  
  101.   { "break", break_builtin, 1, "break [n]",
  102.       "    Exit from within a FOR, WHILE or UNTIL loop.  If N is specified,\n\
  103.     break N levels" },
  104.  
  105.   { "builtin", builtin_builtin, 1, "builtin [shell-builtin [arg ...]]",
  106.       "    Run a shell builtin.  This is useful when you wish to rename a\n\
  107.     shell builtin to be a function, but need the functionality of the\n\
  108.     builtin within the function itself" },
  109.  
  110.   { "bye", exit_builtin, 1, "bye [n]",
  111.       "    Synonym for exit" },
  112.  
  113.   { "cd", cd_builtin, 1, "cd [dir]",
  114.       "    Change the current directory to DIR.  The variable $HOME is the\n\
  115.     default DIR.  The variable $CDPATH defines the search path for\n\
  116.     the directory containing DIR.  Alternative directory names are\n\
  117.     separated by a colon (:).  A null directory name is the same as\n\
  118.     the current directory, i.e. `.'.  If DIR begins with a slash (/),\n\
  119.     then $CDPATH is not used" },
  120.  
  121.   { "command", command_builtin, 1, "command [command [arg ...]]",
  122.       "    Runs COMMAND with ARGS ignoring shell functions.  If you have a\n\
  123.     shell function called `ls', and you wish to call the command\n\
  124.     `ls', you can say \"command ls\"" },
  125.  
  126.   { "continue", continue_builtin, 1, "continue [n]",
  127.       "    Resume the next iteration of the enclosing FOR, WHILE or UNTIL loop.\n\
  128.     If N is specified, resume at the N-th enclosing loop" },
  129.  
  130.   { "declare", declare_builtin, 1, "declare [-[frxi]] name[=value] ...",
  131.       "    Declare variables and/or give them attributes.    If no NAMEs are\n\
  132.     given, then display the values of variables instead.  `-f' means\n\
  133.     to use function names only.  `-r' says to make NAMEs readonly.\n\
  134.     `-x' says to make NAMEs export.     `-i' says that the variable is\n\
  135.     to be treated as an integer; arithmetic evaluation (see `let') will\n\
  136.     be done when the variable is assigned to.  Using `+' instead of `-'\n\
  137.     turns off the attribute instead.  When used in a function, makes\n\
  138.     NAMEs local, as with the `local' command" },
  139.  
  140. #if defined (PUSHD_AND_POPD)
  141.   { "dirs", dirs_builtin, 1, "dirs",
  142.       "    Display the list of currently remembered directories.  Directories\n\
  143.     find their way onto the list with the `pushd' command; you can get\n\
  144.     back up through the list with the `popd' command" },
  145. #endif /* PUSHD_AND_POPD */
  146.  
  147. #if !defined (V9_ECHO)
  148.   { "echo", echo_builtin, 1, "echo [-n] [arg ...]",
  149.       " Output the ARGs.  If -n is specified, then suppress trailing\n\
  150.     newline" },
  151. #else /* V9_ECHO */
  152.   { "echo", echo_builtin, 1, "echo [-n] [-e] [arg ...]",
  153.       "    Output the ARGs.  if -n is specified, the trailing newline is\n\
  154.     suppressed.  If the -e option is given, interpretation of the\n\
  155.     following backslash-escaped characters is turned on:\n\
  156.         \\a    alert (bell)\n\
  157.         \\b    backspace\n\
  158.         \\c    suppress trailing newline\n\
  159.         \\f    form feed\n\
  160.         \\n    new line\n\
  161.         \\r    carriage return\n\
  162.         \\t    horizontal tab\n\
  163.         \\v    vertical tab\n\
  164.         \\\\    backslash\n\
  165.         \\num    the character whose ASCII code is NUM (octal)"},
  166. #endif /* V9_ECHO */
  167.  
  168.   { "enable", enable_builtin, 1, "enable [-n] [name ...]",
  169.       "    Enable and disable builtin shell commands.  This allows\n\
  170.     you to use a disk command which has the same name as a shell\n\
  171.     builtin.  If -n is used, the NAMEs become disabled.  Otherwise\n\
  172.     NAMEs are enabled.  For example, to use the `test' found on your\n\
  173.     path instead of the shell builtin version, you type `enable -n test'" },
  174.  
  175.   { "eval", eval_builtin, 1, "eval [arg ...]",
  176.       "    Read ARGs as input to the shell and execute the resulting command(s)" },
  177.  
  178.   { "exec", exec_builtin, 1, "exec [ [-] file [redirection ...]]",
  179.       "    Exec FILE, replacing this shell with the specified program.\n\
  180.     If FILE is not specified, the redirections take effect in this\n\
  181.     shell.    If the first argument is `-', then place a dash in the\n\
  182.     zeroth arg passed to FILE.  This is what login does.  If the file\n\
  183.     cannot be exec'ed for some reason, the shell exits, unless the\n\
  184.     shell variable \"no_exit_on_failed_exec\" exists" },
  185.  
  186.   { "exit", exit_builtin, 1, "exit [n]",
  187.       "    Exit the shell with a status of N.  If N is omitted, the exit status\n\
  188.     is that of the last command executed" },
  189.  
  190.   { "export", export_builtin, 1, "export [-n] [-p] [-f] [name ...]",
  191.       "    NAMEs are marked for automatic export to the environment of\n\
  192.     subsequently executed commands.     If the -f option is given,\n\
  193.     the NAMEs refer to functions.  If no NAMEs are given, or if `-p'\n\
  194.     is given, a list of all names that are exported in this shell is\n\
  195.     printed.  An argument of `-n' says to remove the export property\n\
  196.     from subsequent NAMEs.  An argument of `--' disables further option\n\
  197.     processing" },
  198.  
  199.   { "fc", fc_builtin, 1, "fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [cmd]",
  200.       "    Fix Command.  In the first form, a range of commands from `first'\n\
  201.     to `last' is selected from the history list.  `First' and `last'\n\
  202.     may be specified as a string (to locate the last command beginning\n\
  203.     with that string) or as a number (an index into the history list,\n\
  204.     where a negative number is used as an offset from the current\n\
  205.     command number).  If `last' is not specified it will be set to\n\
  206.     `first'.  If `first' is not specified it is set to the previous\n\
  207.     command for editing and -16 for listing.  The -n flag suppresses\n\
  208.     the command numbers when listing.  The -r flag reverses the order of\n\
  209.     the commands.  If the -l flag is given, the commands are listed on\n\
  210.     standard output.  Otherwise, the editor given by `ename' is invoked\n\
  211.     on a file containing those commands.  If ename is not given, the\n\
  212.     value of the FCEDIT variable is used, and /bin/ed if that variable\n\
  213.     is not set.  When editing is complete, the edited commands are\n\
  214.     echoed and executed.\n\
  215. \n\
  216.     In the second form, the command is re-executed after the substitution\n\
  217.     old=new is performed.\n\
  218. \n\
  219.     A useful alias to use with this is r='fc -s', so that typing `r cc'\n\
  220.     runs the last command beginning with `cc' and typing `r' re-executes\n\
  221.     the last command" },
  222.  
  223. #if defined (JOB_CONTROL)
  224.   { "fg", fg_builtin, 1, "fg [job_spec]",
  225.       "    Place JOB_SPEC in the foreground, and make it the current job.    If\n\
  226.     JOB_SPEC is not present, the shell's notion of the current job is\n\
  227.     used" },
  228. #endif /* JOB_CONTROL */
  229.  
  230.   { "hash", hash_builtin, 1, "hash [-r] [name ...]",
  231.       "    For each NAME, the full pathname of the command is determined\n\
  232.     and remembered.     The -r option causes the shell to forget all\n\
  233.     remembered locations.  If no arguments are given, information\n\
  234.     about remembered commands is presented" },
  235.  
  236.   { "help", help_builtin, 1, "help [pattern ...]",
  237.      "    Display helpful information about builtin commands.  If\n\
  238.     PATTERN is specified, gives detailed help on all commands\n\
  239.     matching PATTERN, otherwise a list of the builtins is\n\
  240.     printed" },
  241.  
  242.   { "history", history_builtin, 1, "history [n] [-s] [ [-w | -r] [filename]]",
  243.       "    Display the history list with line numbers.  Lines listed with\n\
  244.     with a `*' have been modified.    Argument of N says to list only\n\
  245.     the last N lines.  Argument `-w' means to write out the current\n\
  246.     history file;  `-r' means to read it instead.  If FILENAME is\n\
  247.     given, then use that file, else if $HISTFILE has a value, use\n\
  248.     that, else use ~/.bash_history.     Argument -s performs history\n\
  249.     substitution on the following args" },
  250.  
  251. #if defined (JOB_CONTROL)
  252.   { "jobs", jobs_builtin, 1, "jobs [-lp] [jobspec ...]",
  253.       "    Lists the active jobs.    The -l option lists process id's\n\
  254.     in addition to the normal information; the -p option lists\n\
  255.     process id's only.  JOBSPEC restricts output to that job" },
  256.  
  257.   { "kill", kill_builtin, 1, "kill [-s sigspec | -sigspec] [pid | job]... | -l [signum]",
  258.       "    Send the processes named by PID (or JOB) the signal SIGSPEC.\n\
  259.     If SIGSPEC is not present, then SIGTERM is assumed.  An argument\n\
  260.     of `-l' lists the signal names; if arguments follow `-l' they are\n\
  261.     assumed to be signal numbers for which names should be listed.\n\
  262.     Kill is a builtin for two reasons: it allows job ID's to be used\n\
  263.     instead of pids, and if you run out of processes, you can still\n\
  264.     kill them" },
  265. #endif /* JOB_CONTROL */
  266.  
  267.   { "let", let_builtin, 1, "let arg [arg ...]",
  268.       "    Each ARG is an arithmetic expression to be evaluated.  Evaluation\n\
  269.     is done in long integers with no check for overflow, though division\n\
  270.     by 0 is trapped and flagged as an error.  The following list of\n\
  271.     operators is grouped into levels of equal-precedence operators.\n\
  272.     The levels are listed in order of decreasing precedence.\n\
  273. \n\
  274.         -        unary minus\n\
  275.         !        logical NOT\n\
  276.         * / %        multiplication, division, remainder\n\
  277.         + -        addition, subtraction\n\
  278.         <= >= < >    comparison\n\
  279.         == !=        equality inequality\n\
  280.         =        assignment\n\
  281. \n\
  282.     Shell variables are allowed as operands.  The name of the variable\n\
  283.     is replaced by its value (coerced to a long integer) within\n\
  284.     an expression.    The variable need not have its integer attribute\n\
  285.     turned on to be used in an expression.\n\
  286. \n\
  287.     Operators are evaluated in order of precedence.     Sub-expressions in\n\
  288.     parentheses are evaluated first and may override the precedence\n\
  289.     rules above.\n\
  290. \n\
  291.     If the last ARG evaluates to 0, let returns 1; 0 is returned\n\
  292.     otherwise"},
  293.  
  294.   { "local", local_builtin, 1, "local name[=value] ...",
  295.       "    Create a local variable called NAME, and give it VALUE.     LOCAL\n\
  296.     can only be used within a function; it makes the variable NAME\n\
  297.     have a visible scope restricted to that function and its children" },
  298.  
  299.   { "logout", logout_builtin, 1, "logout",
  300.       "    Logout of a login shell" },
  301.  
  302. #ifdef PUSHD_AND_POPD
  303.   { "popd", popd_builtin, 1, "popd [+n | -n]",
  304.       "    Removes entries from the directory stack.  With no arguments,\n\
  305.     removes the top directory from the stack, and cd's to the new\n\
  306.     top directory.\n\
  307. \n\
  308.     +n    removes the Nth entry counting from the left of the list\n\
  309.         shown by `dirs', starting with zero.  For example: `popd +0'\n\
  310.         removes the first directory, `popd +1' the second.\n\
  311. \n\
  312.     -n    removes the Nth entry counting from the right of the list\n\
  313.         shown by `dirs', starting with zero.  For example: `popd -0'\n\
  314.         removes the last directory, `popd -1' the next to last.\n\
  315. \n\
  316.     You can see the directory stack with the `dirs' command.\n\
  317.     If the variable 'pushd_silent' is not set and the popd command\n\
  318.     was successful, a 'dirs' will be performed as well." },
  319.  
  320.   { "pushd", pushd_builtin, 1, "pushd [dir | +n | -n]",
  321.       "    Adds a directory to the top of the directory stack, or rotates\n\
  322.     the stack, making the new top of the stack the current working\n\
  323.     directory.  With no arguments, exchanges the top two directories.\n\
  324. \n\
  325.     +n    Rotates the stack so that the Nth directory (counting\n\
  326.         from the left of the list shown by `dirs') is at the top.\n\
  327. \n\
  328.     -n    Rotates the stack so that the Nth directory (counting\n\
  329.         from the right) is at the top.\n\
  330. \n\
  331.     dir    adds DIR to the directory stack at the top, making it the\n\
  332.         new current working directory.\n\
  333. \n\
  334.     You can see the directory stack with the `dirs' command.\n\
  335.     If the variable 'pushd_silent' is not set and the pushd command\n\
  336.     was successful, a 'dirs' will be performed as well." },
  337. #endif    /* PUSHD_AND_POPD */
  338.  
  339.   { "pwd", pwd_builtin, 1, "pwd",
  340.       "    Print the current working directory"},
  341.  
  342.   { "read", read_builtin, 1, "read [-r] [name ...]",
  343.       "    One line is read from the standard input, and the first word\n\
  344.     is assigned to the first NAME, the second word to the second NAME,\n\
  345.     etc. with leftover words assigned to the last NAME.  Only the\n\
  346.     characters in $IFS are recognized as word delimiters.  The return\n\
  347.     code is zero, unless end-of-file is encountered.  If the -r option\n\
  348.     is given, a backslash-newline pair (\\\\n) is not ignored, and\n\
  349.     the backslash is considered to be part of the line" },
  350.  
  351.   { "readonly", readonly_builtin, 1, "readonly [-p] [-f] [name ...]",
  352.       "    The given NAMEs are marked readonly and the values of these NAMEs\n\
  353.     may not be changed by subsequent assignment.  If the -f option\n\
  354.     is given, then functions corresponding to the NAMEs are so\n\
  355.     marked.     If no arguments are given, or if `-p' is given, a list of\n\
  356.     all readonly names is printed.  An argument of `--' disables further\n\
  357.     option processing" },
  358.  
  359.   { "return", return_builtin, 1,  "return [n]",
  360.       "    Causes a function to exit with the return value specified by N.\n\
  361.     If N is omitted, the return status is that of the last command" },
  362.  
  363.   { "set", set_builtin, 1, "set [-aefhknotuvxldH] [arg ...]",
  364.       "    -a  Mark variables which are modified or created for export\n\
  365.     -e  Exit immediately if a command exits with a non-zero status\n\
  366.     -f  Disable file name generation (globbing)\n\
  367.     -h  Locate and remember function commands as functions are\n\
  368.         defined.  Function commands are normally looked up when\n\
  369.         the function is executed\n\
  370.     -k  All keyword arguments are placed in the environment for a\n\
  371.         command, not just those that precede the command name\n\
  372.     -m  Job control is enabled\n\
  373.     -n  Read commands but do not execute them\n\
  374.     -o option-name\n\
  375.         Set the variable corresponding to option-name:\n\
  376.         allexport    same as -a\n\
  377.         braceexpand    the shell will perform brace expansion\n\
  378.         emacs        use an emacs-style line editing interface\n\
  379.         errexit        same as -e\n\
  380.         histexpand    same as -H\n\
  381.         ignoreeof    the shell will not exit upon reading EOF\n\
  382.         monitor        same as -m\n\
  383.         noclobber    disallow redirection to existing files\n\
  384.         noexec        same as -n\n\
  385.         noglob        same as -f\n\
  386.         nohash        same as -d\n\
  387.         notify        notify of job termination immediately\n\
  388.         nounset        same as -u\n\
  389.         verbose        same as -v\n\
  390.         vi        use a vi-style line editing interface\n\
  391.         xtrace        same as -x\n\
  392.     -t  Exit after reading and executing one command\n\
  393.     -u  Treat unset variables as an error when substituting\n\
  394.     -v  Print shell input lines as they are read\n\
  395.     -x  Print commands and their arguments as they are executed\n\
  396.     -l  Save and restore the binding of the NAME in a FOR command.\n\
  397.     -d  Disable the hashing of commands that are looked up for execution.\n\
  398.         Normally, commands are remembered in a hash table, and once\n\
  399.         found, do not have to be looked up again\n\
  400.     -H  Enable ! style history substitution.  This flag is on\n\
  401.         by default.\n\
  402.     -C  If set, disallow existing regular files to be overwritten\n\
  403.         by redirection of output.\n\
  404. \n\
  405.     Using + rather than - causes these flags to be turned off.  The\n\
  406.     flags can also be used upon invocation of the shell.  The current\n\
  407.     set of flags may be found in $-.  The remaining ARGs are positional\n\
  408.     parameters and are assigned, in order, to $1, $2, .. $9.  If no\n\
  409.     ARGs are given, all shell variables are printed" },
  410.  
  411.   { "shift", shift_builtin, 1, "shift [n]",
  412.       "    The positional parameters from $n+1 ... are renamed to $1 ....    If\n\
  413.     n is not given, it is assumed to be 1" },
  414.  
  415.   { "source", period_builtin, 1, "source <file>",
  416.       "    An alias for the `.' builtin" },
  417.  
  418. #if defined (JOB_CONTROL)
  419.   { "suspend", suspend_builtin, 1, "suspend [-f]",
  420.       "    Suspend the execution of this shell until it receives a SIGCONT\n\
  421.     signal.     The `-f' if specified says not to complain about this\n\
  422.     being a login shell if it is; just suspend anyway" },
  423. #endif /* JOB_CONTROL */
  424.  
  425.   { "[", test_builtin, 1, "[arg ...]",
  426.       "    Synonym for `test'" },
  427.  
  428.   { "test", test_builtin, 1, "test [expr]",
  429.       "    Exits with a status of 0 (trueness) or 1 (falseness) depending on\n\
  430.     the evaluation of EXPR.     Expressions may be unary or binary.  Unary\n\
  431.     expressions are often used to examine the status of a file.  There\n\
  432.     are string operators as well, and numeric comparison operators.\n\
  433. \n\
  434.     File operators:\n\
  435. \n\
  436.     -b FILE        True if file is block special.\n\
  437.     -c FILE        True if file is character special.\n\
  438.     -d FILE        True if file is a directory.\n\
  439.     -f FILE        True if file is a plain file.\n\
  440.     -g FILE        True if file is set-group-id.\n\
  441.     -L FILE        True if file is a symbolic link.\n\
  442.     -k FILE        True if file has its \"sticky\" bit set.\n\
  443.     -p FILE        True if file is a named pipe.\n\
  444.     -r FILE        True if file is readable by you.\n\
  445.     -s FILE        True if file is not empty.\n\
  446.     -S FILE        True if file is a socket.\n\
  447.     -t [FD]        True if FD is opened on a terminal.  If FD\n\
  448.             is omitted, it defaults to 1 (stdout).\n\
  449.     -u FILE        True if the file is set-user-id.\n\
  450.     -w FILE        True if the file is writable by you.\n\
  451.     -x FILE        True if the file is executable by you.\n\
  452.     -O FILE        True if the file is effectively owned by you.\n\
  453.     -G FILE        True if the file is effectively owned by your group.\n\
  454. \n\
  455.     FILE1 -nt FILE2    True if file1 is newer than (according to\n\
  456.             modification date) file2.\n\
  457. \n\
  458.     FILE1 -ot FILE2 True if file1 is older than file2.\n\
  459. \n\
  460.     FILE1 -ef FILE2    True if file1 is a hard link to file2.\n\
  461. \n\
  462.     String operators:\n\
  463. \n\
  464.     -z STRING    True if string is empty.\n\
  465.     -n STRING\n\
  466.    or            True if string is not empty.\n\
  467.     STRING\n\
  468.     STRING1 = STRING2\n\
  469.             True if the strings are equal.\n\
  470.     STRING1 != STRING2\n\
  471.             True if the strings are not equal.\n\
  472. \n\
  473.     Other operators:\n\
  474. \n\
  475.     ! EXPR        True if expr is false.\n\
  476.     EXPR1 -a EXPR2    True if both expr1 AND expr2 are true.\n\
  477.     EXPR1 -o EXPR2    True if either expr1 OR expr2 is true.\n\
  478. \n\
  479.     arg1 OP arg2\n\
  480.     OP is one of -eq, -ne, -lt, -le, -gt, ge.\n\
  481.             Arithmetic binary operators return true if ARG1\n\
  482.             is equal, not-equal, less-than, less-than-or-equal,\n\
  483.             greater-than, or greater-than-or-equal than arg2" },
  484.  
  485.   { "times", times_builtin, 1, "times",
  486.       "    Print the accumulated user and system times for processes run from\n\
  487.     the shell" },
  488.  
  489.   { "trap", trap_builtin, 1, "trap [arg] [signal_spec]",
  490.       "    The command ARG is to be read and executed when the shell receives\n\
  491.     signal(s) SIGNAL_SPEC.    If ARG is absent all specified signals are\n\
  492.     reset to their original values.     If ARG is the null string this\n\
  493.     signal is ignored by the shell and by the commands it invokes.    If\n\
  494.     SIGNAL_SPEC is EXIT (0) the command ARG is executed on exit from\n\
  495.     the shell.  The trap command with no arguments prints the list of\n\
  496.     commands associated with each signal number.  SIGNAL_SPEC is either\n\
  497.     a signal name in <signal.h>, or a signal number.  The syntax `trap -l'\n\
  498.     prints a list of signal names and their corresponding numbers.\n\
  499.     Note that a signal can be sent to the shell with \"kill -signal $$\"" },
  500.  
  501.   { "type", type_builtin, 1, "type [-all] [-type | -path] [name ...]",
  502.       "    For each NAME, indicate how it would be interpreted if used as a\n\
  503.     command name.\n\
  504. \n\
  505.     If the -type flag is used, returns a single word which is one of\n\
  506.     `alias', `function', `builtin', `file' or `', if NAME is an\n\
  507.     alias, shell function, shell builtin, disk file, or unfound,\n\
  508.     respectively.\n\
  509. \n\
  510.     If the -path flag is used, either returns the name of the disk file\n\
  511.     that would be exec'ed, or nothing if -type wouldn't return `file'.\n\
  512. \n\
  513.     If the -all flag is used, returns all of the places that contain\n\
  514.     an executable named `file'.  This includes aliases and functions,\n\
  515.     if and only if the -path flag is not also used" },
  516.  
  517.   { "typeset", declare_builtin, 1, "typeset [-frxi] [name[=word] ...]",
  518.       "    Obsolete.  See `declare'" },
  519.  
  520.   { "ulimit", ulimit_builtin, 1, "ulimit [-acdmstfpn [limit]]",
  521.       " Ulimit provides control over the resources available to processes\n\
  522.     started by the shell, on systems that allow such control.  If an\n\
  523.     option is given, it is interpreted as follows:\n\
  524. \n\
  525.         -a    all current limits are reported\n\
  526.         -c    the maximum size of core files created\n\
  527.         -d    the maximum size of a process's data segment\n\
  528.         -m    the maximum resident set size\n\
  529.         -s    the maximum stack size\n\
  530.         -t    the maximum amount of cpu time in seconds\n\
  531.         -f    the maximum size of files created by the shell\n\
  532.         -p    the pipe buffer size\n\
  533.         -n    the maximum number of open file descriptors\n\
  534. \n\
  535.     If LIMIT is given, it is the new value of the specified resource.\n\
  536.     Otherwise, the current value of the specified resource is printed.\n\
  537.     If no option is given, then -f is assumed.  Values are in 1k\n\
  538.     increments, except for -t, which is in seconds, and -p, which is in\n\
  539.     increments of 512 bytes" },
  540.  
  541.   { "umask", umask_builtin, 1, "umask [-S] [mode]",
  542.       "    The user file-creation mask is set to MODE.  If MODE is omitted, or\n\
  543.     if `-S' is supplied, the current value of the mask is printed.\n\
  544.     The `-S' option makes the output symbolic; otherwise an octal number\n\
  545.     is output.  If MODE begins with a digit, it is interpreted as an\n\
  546.     octal number, otherwise it is a symbolic mode string like that\n\
  547.     accepted by chmod(1)" },
  548.  
  549.   { "unalias", unalias_builtin, 1, "unalias [name ...]",
  550.       "    Remove NAMEs from the list of defined aliases" },
  551.  
  552.   { "unset", unset_builtin, 1, "unset [-f] [-v] [name ...]",
  553.       "    For each NAME, remove the corresponding variable or, given the\n\
  554.     -f option, function.  Note that PATH and IFS cannot be unset" },
  555.  
  556.   { "wait", wait_builtin, 1, "wait [n]",
  557.       "    Wait for the specified process and report its termination\n\
  558.     status.     If N is not given, all currently active child processes\n\
  559.     are waited for, and the return code is zero.  N may be a process\n\
  560.     ID or a job specification; if a job spec is given, all processes\n\
  561.     in the job's pipeline are waited for" },
  562.  
  563.   /* This marks the end of the functions which are builtins per-se.  The
  564.      following are actually parser constructs. */
  565.   { "for", (Function *)0x0, 1, "for NAME [in WORDS ...]; do COMMANDS; done",
  566.       "    The `for' loop executes a sequence of commands for each member in a\n\
  567.     list of items.    If \"in WORDS ...\" is not present, then \"in $*\" is\n\
  568.     assumed.  For each element in WORDS, NAME is set to that element, and\n\
  569.     the COMMANDS are executed" },
  570.  
  571.   { "case", (Function *)0x0, 1, "case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac",
  572.       "    Selectively execute COMMANDS based upon WORD matching PATTERN.    The\n\
  573.     `|' is used to separate multiple patterns" },
  574.  
  575.   { "if", (Function *)0x0, 1, "if COMMANDS; then COMMANDS; [else COMMANDS;] fi",
  576.       "    `if' executes the `then' COMMANDS only if the final command in the `if'\n\
  577.     COMMANDS has an exit status of zero" },
  578.  
  579.   { "while", (Function *)0x0, 1, "while COMMANDS; do COMMANDS; done",
  580.       "    Expand and execute COMMANDS as long as the final command in the `while'\n\
  581.     COMMANDS has an exit status of zero" },
  582.  
  583.   { "until", (Function *)0x0, 1, "until COMMANDS; do COMMANDS; done",
  584.       "    Expand and execute COMMANDS as long as the final command in the `until'\n\
  585.     COMMANDS has an exit status which is not zero" },
  586.  
  587.   { "function", (Function *)0x0, 1, "function NAME { COMMANDS ; } or NAME () { COMMANDS ; }",
  588.       "    Create a simple command invoked by NAME which runs COMMANDS.  Arguments\n\
  589.     on the command line along with NAME are passed to the function as\n\
  590.     $0 .. $n" },
  591.   { "{ ... }", (Function *)0x0, 1, "{ COMMANDS }",
  592.       "    Run a set of commands in a group.  This is one way to redirect an\n\
  593.     entire set of commands" },
  594.  
  595. #if defined (JOB_CONTROL)
  596.   { "%", (Function *)0x0, 1, "%[DIGITS | WORD] [&]",
  597.       "    This is similar to the `fg' command.  Resume a stopped or background\n\
  598.     job.  If you specifiy DIGITS, then that job is used.  If you specify\n\
  599.     WORD, then the job whose name begins with WORD is used.     Following\n\
  600.     the job specification with a `&' places the job in the background" },
  601. #endif /* JOB_CONTROL */
  602.  
  603.   { (char *)0x0, (Function *)0x0, 0, (char *)0x0, (char *)0x0 }
  604.  
  605. };
  606.  
  607. /* Enable the shell command NAME.  If DISABLE_P is non-zero, then
  608.    disable NAME instead. */
  609. enable_shell_command (name, disable_p)
  610.      char *name;
  611.      int disable_p;
  612. {
  613.   register int i;
  614.   int found = 0;
  615.  
  616.   for (i = 0; shell_builtins[i].function; i++)
  617.     if (strcmp (name, shell_builtins[i].name) == 0)
  618.       {
  619.     found++;
  620.     shell_builtins[i].enabled = !disable_p;
  621.       }
  622.  
  623.   return (found);
  624. }
  625.  
  626. /* Enable/disable shell commands present in LIST.  If list is not specified,
  627.    then print out a list of shell commands showing which are enabled and
  628.    which are disabled. */
  629. enable_builtin (list)
  630.      WORD_LIST *list;
  631. {
  632.   int result = 0, any_failed = 0;
  633.  
  634.   if (!list)
  635.     {
  636.       register int i;
  637.  
  638.       for (i = 0; shell_builtins[i].function; i++)
  639.     printf ("enable %s%s\n", shell_builtins[i].enabled ? "" : "-n ",
  640.         shell_builtins[i].name);
  641.     }
  642.   else
  643.     {
  644.       int disable_p = (strcmp (list->word->word, "-n") == 0);
  645.  
  646.       if (disable_p)
  647.     list = list->next;
  648.  
  649.       while (list)
  650.     {
  651.       result = enable_shell_command (list->word->word, disable_p);
  652.       if (!result)
  653.         {
  654.           builtin_error ("%s: not a shell builtin", list->word->word);
  655.           any_failed++;
  656.         }
  657.       list = list->next;
  658.     }
  659.     }
  660.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  661. }
  662.  
  663. /* Print out a list of the known functions in the shell, and what they do.
  664.    If LIST is supplied, print out the list which matches for each pattern
  665.    specified. */
  666. help_builtin (list)
  667.      WORD_LIST *list;
  668. {
  669.   if (!list)
  670.     {
  671.       register int j, i = 0;
  672.       char blurb[256];
  673.  
  674.       show_shell_version ();
  675.       printf (
  676. "Shell commands that are defined internally.  Type `help' to see this list.\n\
  677. Type `help name' to find out more about the function `name'.\n\
  678. Use `info bash' to find out more about the shell in general.\n\
  679. \n\
  680. A star (*) next to a name means that the command is disabled.\n\
  681. \n");
  682.  
  683.       for (i = 0; shell_builtins[i].name; i++)
  684.     {
  685.       sprintf (blurb, "%c%s", shell_builtins[i].enabled ? ' ' : '*',
  686.            shell_builtins[i].short_doc);
  687.  
  688.       blurb[35] = '\0';
  689.       printf ("%s", blurb);
  690.  
  691.       if (i % 2)
  692.         printf ("\n");
  693.       else
  694.         for (j = strlen (blurb); j < 35; j++)
  695.           putc (' ', stdout);
  696.  
  697.     }
  698.       if (i % 2)
  699.     printf ("\n");
  700.     }
  701.   else
  702.     {
  703.       int match_found = 0;
  704.       char *pattern = "";
  705.  
  706.       if (glob_pattern_p (list->word->word))
  707.     {
  708.       printf ("Shell commands matching keyword%s `",
  709.           list->next ? "s" : "");
  710.       print_word_list (list, ", ");
  711.       printf ("'\n\n");
  712.     }
  713.       while (list)
  714.     {
  715.       register int i = 0, plen;
  716.  
  717.       pattern = list->word->word;
  718.       plen = strlen (pattern);
  719.  
  720.       while (shell_builtins[i].name)
  721.         {
  722.           if (glob_match (pattern, shell_builtins[i].name, 0) ||
  723.           strnicmp (pattern, shell_builtins[i].name, plen) == 0)
  724.         {
  725.           printf ("%s: %s\n%s.\n",
  726.               shell_builtins[i].name, shell_builtins[i].short_doc,
  727.               shell_builtins[i].long_doc);
  728.           match_found++;
  729.         }
  730.           i++;
  731.         }
  732.       list = list->next;
  733.     }
  734.       if (!match_found)
  735.     {
  736.       printf ("No help topics match `%s'.  Try `help help'.\n", pattern);
  737.       fflush (stdout);
  738.       return (EXECUTION_FAILURE);
  739.     }
  740.     }
  741.   fflush (stdout);
  742.   return (EXECUTION_SUCCESS);
  743. }
  744.  
  745. /* Do nothing.    This command is a no-op. */
  746. colon_builtin ()
  747. {
  748.   return (EXECUTION_SUCCESS);
  749. }
  750.  
  751. /* Read and execute commands from the file passed as argument.    Guess what.
  752.    This cannot be done in a subshell, since things like variable assignments
  753.    take place in there.     So, I open the file, place it into a large string,
  754.    close the file, and then execute the string. */
  755. period_builtin (list)
  756.      WORD_LIST *list;
  757. {
  758.   int result = (EXECUTION_SUCCESS);
  759.   int push_dollar_vars (), pop_dollar_vars ();
  760.  
  761.   if (list)
  762.     {
  763.       int fd;            /* File descriptor. */
  764.       int tt;            /* Temporary result. */
  765.       char *string;        /* String to execute. */
  766.       char *filename, *tempfile;
  767.       extern char *find_path_file ();
  768.       struct stat finfo;
  769.  
  770.       tempfile = find_path_file (list->word->word);
  771.  
  772.       if (!tempfile)
  773.     tempfile = savestring (list->word->word);
  774.  
  775.       filename = (char *)alloca (1 + strlen (tempfile));
  776.       strcpy (filename, tempfile);
  777.       free (tempfile);
  778.  
  779.       if (stat (filename, &finfo) == -1 ||
  780.       (fd = open (filename, O_RDONLY)) == -1)
  781.     goto file_error_exit;
  782.  
  783.       string = (char *)xmalloc    (1 + finfo.st_size);
  784.       tt = read (fd, string, finfo.st_size);
  785.  
  786.       /* Close the open file, preserving the state of errno. */
  787.       { int temp = errno; close (fd); errno = temp; }
  788.  
  789.       if (tt != finfo.st_size)
  790.     {
  791.       free (string);
  792.     file_error_exit:
  793.       file_error (filename);
  794.       return (EXECUTION_FAILURE);
  795.     }
  796.  
  797.       push_dollar_vars ();
  798.       remember_args (list->next, 1);
  799.       begin_unwind_frame ("file_sourceing");
  800.       add_unwind_protect (pop_dollar_vars, (char *)NULL);
  801.       string[finfo.st_size] = '\0';
  802.       result = parse_and_execute (string, filename);
  803.       run_unwind_frame ("file_sourceing");
  804.     }
  805.   return (result);
  806. }
  807.  
  808. int special_source_interrupt = 0;
  809. jmp_buf special_buf;
  810.  
  811. /* Parse and execute the commands in STRING.  Returns whatever
  812.    execute_command () returns.  This frees STRING. */
  813. int
  814. parse_and_execute (string, from_file)
  815.      char *string;
  816.      char *from_file;
  817. {
  818.   extern int remember_on_history;
  819.   extern int history_expansion_inhibited;
  820.   extern int indirection_level;
  821.   extern int builtin_pipe_in, builtin_pipe_out;
  822.   char *indirection_level_string ();
  823.  
  824.   int old_interactive = interactive;
  825.   int old_remember_on_history = remember_on_history;
  826.   int old_history_expansion_inhibited = history_expansion_inhibited;
  827.   int last_result = EXECUTION_SUCCESS;
  828.   char *orig_string = string;
  829.   extern COMMAND *global_command;
  830.  
  831.   jmp_buf old_special_buf, old_top_level;
  832.  
  833.   push_stream ();
  834.   interactive = 0;
  835.   indirection_level++;
  836.  
  837.   /* We have to catch SIGINT, especially if we were interactive, otherwise
  838.      a ^C in the wrong place can kill the shell.  We need to flag it for
  839.      throw_to_top_level (), and we need a place for throw_to_top_level to
  840.      throw to.  If we continue without throwing out of throw_to_top_level (),
  841.      the net effect is to make certain dot scripts uninterruptible.  We
  842.      simply jump to the exit code in this function; the same place the
  843.      original code that checks interrupt_state would have jumped to.
  844.      special_source_input needs to be decremented in the exit code below --
  845.      since interrupt_state is not cleared here, another call to
  846.      throw_to_top_level() will be made after this function returns, and we
  847.      want a kind of stack of calls to parse_and_execute to unwind. */
  848.  
  849.   special_source_interrupt++;
  850.   bcopy ((char *) special_buf, (char *)old_special_buf, sizeof (jmp_buf));
  851.   bcopy ((char *) top_level, (char *)old_top_level, sizeof (jmp_buf));
  852.  
  853.   if (setjmp (special_buf) == 1)
  854.     goto out;
  855.  
  856.   /* We don't remember text read by the shell this way on
  857.      the history list, and we don't use !$ in shell scripts. */
  858.   remember_on_history = 0;
  859.   history_expansion_inhibited = 1;
  860.  
  861.   with_input_from_string (string, from_file);
  862.   {
  863.     extern char *yy_input_dev;
  864.     COMMAND *command;
  865.     int code;
  866.  
  867.     while (*yy_input_dev)
  868.       {
  869.     if (interrupt_state)
  870.       {
  871.         last_result = EXECUTION_FAILURE;
  872.         break;
  873.       }
  874.  
  875.     /* Provide a location for functions which `longjmp (top_level)' to
  876.        jump to.  This prevents errors in sunbstitution from restarting
  877.        the reader loop directly, for example. */
  878.     if (code = setjmp (top_level))
  879.       {
  880.         switch (code)
  881.           {
  882.         case FORCE_EOF:
  883.             case EXITPROG:
  884.           run_unwind_frame ("pe_dispose");
  885.           goto out;
  886.  
  887.         case DISCARD:
  888.           run_unwind_frame ("pe_dispose");
  889.           continue;
  890.  
  891.         default:
  892.           programming_error ("bad jump to top_level: %d", code);
  893.           break;
  894.           }
  895.       }
  896.       
  897.     if (parse_command () == 0)
  898.       {
  899.         if ((command = global_command) != (COMMAND *)NULL)
  900.           {
  901.         extern struct fd_bitmap *new_fd_bitmap ();
  902.         extern void dispose_fd_bitmap ();
  903.         struct fd_bitmap *bitmap;
  904.  
  905.         bitmap = new_fd_bitmap (FD_BITMAP_SIZE);
  906.         begin_unwind_frame ("pe_dispose");
  907.         add_unwind_protect (dispose_fd_bitmap, bitmap);
  908.  
  909.         global_command = (COMMAND *)NULL;
  910.  
  911.         if (builtin_pipe_in != NO_PIPE)
  912.           bitmap->bitmap[builtin_pipe_in] = 1;
  913.  
  914.         if (builtin_pipe_out != NO_PIPE)
  915.           bitmap->bitmap[builtin_pipe_out] = 1;
  916.  
  917.         last_result =
  918.           execute_command_internal
  919.             (command, 0, NO_PIPE, NO_PIPE, bitmap);
  920.  
  921.         dispose_command (command);
  922.         run_unwind_frame ("pe_dispose");
  923.           }
  924.       }
  925.     else
  926.       {
  927.         last_result = EXECUTION_FAILURE;
  928.  
  929.         /* Since we are shell compatible, syntax errors in a script
  930.            abort the execution of the script.  Right? */
  931.         break;
  932.       }
  933.       }
  934.   }
  935.  
  936.  out:
  937.  
  938.   remember_on_history = old_remember_on_history;
  939.   history_expansion_inhibited = old_history_expansion_inhibited;
  940.   interactive = old_interactive;
  941.   pop_stream ();
  942.   indirection_level--;
  943.   if (orig_string)
  944.     free (orig_string);
  945.  
  946.   /* Make sure this is off, because a call to throw_to_top_level will
  947.      eventually be made, and we don't want this hanging around to screw
  948.      it up. */
  949.   bcopy ((char *)old_special_buf, (char *)special_buf, sizeof (jmp_buf));
  950.   bcopy ((char *)old_top_level, (char *)top_level, sizeof (jmp_buf));
  951.   special_source_interrupt--;
  952.  
  953.   if (interrupt_state && special_source_interrupt == 0)
  954.     throw_to_top_level ();
  955.  
  956.   return (last_result);
  957. }
  958.  
  959. /* Set up to break x levels, where x defaults to 1, but can be specified
  960.    as the first argument. */
  961.  
  962. /* The depth of while's and until's. */
  963. int loop_level = 0;
  964.  
  965. /* Non-zero when a "break" instruction is encountered. */
  966. int breaking = 0;
  967.  
  968. /* Return non-zero if a break or continue command would be okay.
  969.    Print an error message if break or continue is meaningless here. */
  970. check_loop_level ()
  971. {
  972.   extern char *this_command_name;
  973.  
  974. #ifdef BREAK_COMPLAINS
  975.   if (!loop_level)
  976.     builtin_error ("Only meaningful in a `for', `while', or `until' loop");
  977. #endif
  978.   return (loop_level);
  979. }
  980.  
  981. break_builtin (list)
  982.      WORD_LIST *list;
  983. {
  984.  
  985.   if (!check_loop_level ())
  986.     return (EXECUTION_FAILURE);
  987.  
  988.   breaking = get_numeric_arg (list);
  989.  
  990.   if (breaking < 0)
  991.     breaking = 0;
  992.  
  993.   if (breaking > loop_level)
  994.     breaking = loop_level;
  995.  
  996.   return (EXECUTION_SUCCESS);
  997. }
  998.  
  999. /* Set up to continue x levels, where x defaults to 1, but can be specified
  1000.    as the first argument. */
  1001.  
  1002. /* Non-zero when we have encountered a continue instruction. */
  1003. int continuing = 0;
  1004.  
  1005. continue_builtin (list)
  1006.      WORD_LIST *list;
  1007. {
  1008.  
  1009.   if (!check_loop_level ())
  1010.     return (EXECUTION_FAILURE);
  1011.  
  1012.   continuing = get_numeric_arg (list);
  1013.  
  1014.   if (continuing < 0)
  1015.     continuing = 0;
  1016.  
  1017.   if (continuing > loop_level)
  1018.     continuing = loop_level;
  1019.  
  1020.   return (EXECUTION_SUCCESS);
  1021. }
  1022.  
  1023. /* Read a numeric arg for this_command_name, the name of the shell builtin
  1024.    that wants it.  LIST is the word list that the arg is to come from. */
  1025. get_numeric_arg (list)
  1026.      WORD_LIST *list;
  1027. {
  1028.   int count = 1;
  1029.  
  1030.   if (list)
  1031.     {
  1032.       if (sscanf (list->word->word, "%d", &count) != 1)
  1033.     {
  1034.       builtin_error ("bad non-numeric arg `%s'", list->word->word);
  1035.       throw_to_top_level ();
  1036.     }
  1037.       no_args (list->next);
  1038.     }
  1039.   return (count);
  1040. }
  1041.  
  1042. /* Change the current working directory to the first word in LIST, or
  1043.    to $HOME if there is no LIST.  Do nothing in the case that there is
  1044.    no $HOME nor LIST. If the variable CDPATH exists, use that as the search
  1045.    path for finding the directory.  If all that fails, and the variable
  1046.    `cdable_vars' exists, then try the word as a variable name.    If that
  1047.    variable has a value, then cd to the value of that variable. */
  1048.  
  1049. /* By default, follow the symbolic links as if they were real directories
  1050.    while hacking the `cd' command.  This means that `cd ..' moves up in
  1051.    the string of symbolic links that make up the current directory, instead
  1052.    of the absolute directory.  The shell variable `nolinks' controls this
  1053.    flag. */
  1054. int follow_symbolic_links = 1;
  1055.  
  1056. /* In order to keep track of the working directory, we have this static
  1057.    variable hanging around. */
  1058. static char *the_current_working_directory = (char *)NULL;
  1059.  
  1060. cd_builtin (list)
  1061.      WORD_LIST *list;
  1062. {
  1063.   char *dirname;
  1064.  
  1065.   {
  1066.     extern int restricted;
  1067.     if (restricted)
  1068.       {
  1069.     builtin_error ("Privileged command");
  1070.     return (EXECUTION_FAILURE);
  1071.       }
  1072.   }
  1073.  
  1074.   if (list)
  1075.     {
  1076.       char *extract_colon_unit ();
  1077.       char *path_string = get_string_value ("CDPATH");
  1078.       char *path;
  1079.       int index = 0;
  1080.  
  1081.       dirname = list->word->word;
  1082.  
  1083.       if (path_string && !absolute_pathname (dirname))
  1084.     {
  1085.       while ((path = extract_colon_unit (path_string, &index)))
  1086.         {
  1087.           char *dir;
  1088.  
  1089.           if (*path && (*path == '~'))
  1090.         {
  1091.           char *tilde_expand (), *te_string = tilde_expand (path);
  1092.  
  1093.           free (path);
  1094.           path = te_string;
  1095.         }
  1096.  
  1097.           if (!*path)
  1098.         {
  1099.           free (path);
  1100.           path = savestring ("."); /* by definition. */
  1101.         }
  1102.  
  1103.           dir = (char *)alloca (2 + strlen (dirname) + strlen (path));
  1104.  
  1105.           if (!dir)
  1106.         abort ();
  1107.  
  1108.           strcpy (dir, path);
  1109.           if (path[strlen (path) - 1] != '/')
  1110.         strcat (dir, "/");
  1111.           strcat (dir, dirname);
  1112.           free (path);
  1113.  
  1114.           if (change_to_directory (dir))
  1115.         {
  1116.           if (strncmp (dir, "./", 2) != 0)
  1117.             printf ("%s\n", dir);
  1118.           dirname = dir;
  1119.  
  1120.           goto bind_and_exit;
  1121.         }
  1122.         }
  1123.     }
  1124.  
  1125.       if (!change_to_directory (dirname))
  1126.     {
  1127.       /* Maybe this is `cd -', equivalent to `cd $OLDPWD' */
  1128.       if (dirname[0] == '-' && dirname[1] == '\0')
  1129.         {
  1130.           char *t = get_string_value ("OLDPWD");
  1131.  
  1132.           if (t && change_to_directory (t))
  1133.         goto bind_and_exit;
  1134.         }
  1135.  
  1136.       /* If the user requests it, then perhaps this is the name of
  1137.          a shell variable, whose value contains the directory to
  1138.          change to.     If that is the case, then change to that
  1139.          directory. */
  1140.       if (find_variable ("cdable_vars"))
  1141.         {
  1142.           char *t = get_string_value (dirname);
  1143.  
  1144.           if (t && change_to_directory (t))
  1145.         {
  1146.           printf ("%s\n", t);
  1147.           goto bind_and_exit;
  1148.         }
  1149.         }
  1150.  
  1151.       file_error (dirname);
  1152.       return (EXECUTION_FAILURE);
  1153.     }
  1154.       goto bind_and_exit;
  1155.     }
  1156.   else
  1157.     {
  1158.       dirname = get_string_value ("HOME");
  1159.  
  1160.       if (!dirname)
  1161.     return (EXECUTION_FAILURE);
  1162.  
  1163.       if (!change_to_directory (dirname))
  1164.     {
  1165.       file_error (dirname);
  1166.       return (EXECUTION_FAILURE);
  1167.     }
  1168.  
  1169.     bind_and_exit:
  1170.       {
  1171.     char *get_working_directory (), *get_string_value ();
  1172.     char *directory = get_working_directory ("cd");
  1173.  
  1174.     bind_variable ("OLDPWD", get_string_value ("PWD"));
  1175.     bind_variable ("PWD", directory);
  1176.  
  1177.     if (directory)
  1178.       free (directory);
  1179.       }
  1180.       return (EXECUTION_SUCCESS);
  1181.     }
  1182. }
  1183.  
  1184. /* Do the work of changing to the directory NEWDIR.  Handle symbolic
  1185.    link following, etc. */
  1186. change_to_directory (newdir)
  1187.      char *newdir;
  1188. {
  1189.   char *get_working_directory (), *make_absolute ();
  1190.   char *t;
  1191.  
  1192.   if (follow_symbolic_links)
  1193.     {
  1194.       if (!the_current_working_directory)
  1195.     {
  1196.       t = get_working_directory ("cd_links");
  1197.       if (t)
  1198.         free (t);
  1199.     }
  1200.  
  1201.       if (the_current_working_directory)
  1202.     t = make_absolute (newdir, the_current_working_directory);
  1203.       else
  1204.     t = savestring (newdir);
  1205.  
  1206.       /* Get rid of trailing `/'. */
  1207.       {
  1208.     register int len_t = strlen (t);
  1209.     if (len_t > 1)
  1210.       {
  1211.         --len_t;
  1212.         if (t[len_t] == '/')
  1213.           t[len_t] = '\0';
  1214.       }
  1215.       }
  1216.  
  1217.       if (chdir (t) < 0)
  1218.     {
  1219.       free (t);
  1220.       return (0);
  1221.     }
  1222.  
  1223.       if (the_current_working_directory)
  1224.     strcpy (the_current_working_directory, t);
  1225.  
  1226.       free (t);
  1227.       return (1);
  1228.     }
  1229.   else
  1230.     {
  1231.       if (chdir (newdir) < 0)
  1232.     return (0);
  1233.       else
  1234.     return (1);
  1235.     }
  1236. }
  1237.  
  1238. /* Return a consed string which is the current working directory.
  1239.    FOR_WHOM is the name of the caller for error printing.  */
  1240. char *
  1241. get_working_directory (for_whom)
  1242.      char *for_whom;
  1243. {
  1244.   if (!follow_symbolic_links)
  1245.     {
  1246.       if (the_current_working_directory)
  1247.     free (the_current_working_directory);
  1248.  
  1249.       the_current_working_directory = (char *)NULL;
  1250.     }
  1251.  
  1252.   if (!the_current_working_directory)
  1253.     {
  1254.       char *directory, *getwd ();
  1255.  
  1256.       the_current_working_directory = (char *)xmalloc (MAXPATHLEN);
  1257.       directory = getwd (the_current_working_directory);
  1258.       if (!directory)
  1259.     {
  1260.       fprintf (stderr, "%s: %s\n",
  1261.            for_whom, the_current_working_directory);
  1262.       free (the_current_working_directory);
  1263.       the_current_working_directory = (char *)NULL;
  1264.       return (char *)NULL;
  1265.     }
  1266.     }
  1267.  
  1268.   return (savestring (the_current_working_directory));
  1269. }
  1270.  
  1271.  
  1272. /* Print the words in LIST to standard output.    If the first word is
  1273.    `-n', then don't print a trailing newline.  We also support the
  1274.    echo syntax from Version 9 unix systems. */
  1275. echo_builtin (list)
  1276.      WORD_LIST *list;
  1277. {
  1278.   int display_return = 1, do_v9 = 0;
  1279.  
  1280. /* System V machines already have a /bin/sh with a v9 behaviour.  We
  1281.    give Bash the identical behaviour for these machines so that the
  1282.    existing system shells won't barf. */
  1283. #if defined (V9_ECHO) && defined (USG)
  1284.     do_v9 = 1;
  1285. #endif
  1286.  
  1287.   while (list && list->word->word[0] == '-')
  1288.     {
  1289.       char *temp = &(list->word->word[1]);
  1290.  
  1291.       if (!*temp)
  1292.     goto just_echo;
  1293.  
  1294.       while (*temp)
  1295.     {
  1296.       if (*temp == 'n')
  1297.         display_return = 0;
  1298. #if defined (V9_ECHO)
  1299.       else if (*temp == 'e')
  1300.         do_v9 = 1;
  1301. #if defined (USG)
  1302.       else if (*temp == 'E')
  1303.         do_v9 = 0;
  1304. #endif /* USG */
  1305. #endif /* V9_ECHO */
  1306.       else
  1307.         goto just_echo;
  1308.  
  1309.       temp++;
  1310.     }
  1311.       list = list->next;
  1312.     }
  1313.  
  1314. just_echo:
  1315.  
  1316.   if (list)
  1317.     {
  1318. #ifdef V9_ECHO
  1319.       if (do_v9)
  1320.     {
  1321.       while (list)
  1322.         {
  1323.           register char *s = list->word->word;
  1324.           register int c;
  1325.  
  1326.           while (c = *s++)
  1327.         {
  1328.           if (c == '\\' && *s)
  1329.             {
  1330.               switch (c = *s++)
  1331.             {
  1332.             case 'a': c = '\007'; break;
  1333.             case 'b': c = '\b'; break;
  1334.             case 'c': display_return = 0; continue;
  1335.             case 'f': c = '\f'; break;
  1336.             case 'n': c = '\n'; break;
  1337.             case 'r': c = '\r'; break;
  1338.             case 't': c = '\t'; break;
  1339.             case 'v': c = (int) 0x0B; break;
  1340.             case '0': case '1': case '2': case '3':
  1341.             case '4': case '5': case '6': case '7':
  1342.               c -= '0';
  1343.               if (*s >= '0' && *s <= '7')
  1344.                 c = c * 8 + (*s++ - '0');
  1345.               if (*s >= '0' && *s <= '7')
  1346.                 c = c * 8 + (*s++ - '0');
  1347.               break;
  1348.             case '\\': break;
  1349.             default:  putchar ('\\'); break;
  1350.             }
  1351.             }
  1352.           putchar(c);
  1353.         }
  1354.           list = list->next;
  1355.           if (list)
  1356.         putchar(' ');
  1357.         }
  1358.     }
  1359.       else
  1360. #endif    /* V9_ECHO */
  1361.     print_word_list (list, " ");
  1362.     }
  1363.   if (display_return)
  1364.     printf ("\n");
  1365.   fflush (stdout);
  1366.   return (EXECUTION_SUCCESS);
  1367. }
  1368.  
  1369. /* Parse the string that these words make, and execute the command found. */
  1370. eval_builtin (list)
  1371.      WORD_LIST *list;
  1372. {
  1373.   char *string_list ();
  1374.   int result;
  1375.  
  1376.   /* Note that parse_and_execute () free ()'s what it is passed. */
  1377.   if (list)
  1378.     result = parse_and_execute (string_list (list), "eval");
  1379.   else
  1380.     result = EXECUTION_SUCCESS;
  1381.   return (result);
  1382. }
  1383.  
  1384. /* Defined in execute_cmd.c */
  1385. extern REDIRECT *redirection_undo_list;
  1386.  
  1387. int
  1388. exec_builtin (list)
  1389.      WORD_LIST *list;
  1390. {
  1391.   extern char *find_user_command ();
  1392.  
  1393.   maybe_make_export_env ();
  1394.  
  1395.   /* First, let the redirections remain. */
  1396.   dispose_redirects (redirection_undo_list);
  1397.   redirection_undo_list = (REDIRECT *)NULL;
  1398.  
  1399.   if (!list)
  1400.     return (EXECUTION_SUCCESS);
  1401.   else
  1402.     {
  1403.       /* Otherwise, execve the new command with args. */
  1404.       char *command, **args;
  1405.       int dash_name = 0;
  1406.  
  1407.       if (strcmp (list->word->word, "-") == 0)
  1408.     {
  1409.       /* The user would like to exec this command as if it was a
  1410.          login command.  Do so. */
  1411.       list = list->next;
  1412.       dash_name++;
  1413.     }
  1414.  
  1415.       args = (char **)make_word_array (list);
  1416.  
  1417.       /* A command with a slash anywhere in its name is not looked up in
  1418.      the search path. */
  1419.       if (absolute_program (args[0]))
  1420.     command = args[0];
  1421.       else
  1422.     command = find_user_command (args[0]);
  1423.       if (!command)
  1424.     {
  1425.       builtin_error ("%s: not found", args[0]);
  1426.       goto failed_exec;
  1427.     }
  1428.  
  1429.       command = (char *)full_pathname (command);
  1430.       /* If the user wants this to look like a login shell, then
  1431.      pass the full pathname in argv[0]. */
  1432.       if (dash_name)
  1433.     {
  1434.       char *new_name = (char *)xmalloc (1 + strlen (command));
  1435.       strcpy (new_name, "-");
  1436.       strcat (new_name, command);
  1437.       args[0] = new_name;
  1438.     }
  1439.  
  1440.       /* Decrement SHLVL by 1 so a new shell started here has the same value,
  1441.      preserving the appearance.  After we do that, we need to change the
  1442.      exported environment to include the new value. */
  1443.       adjust_shell_level (-1);
  1444.       maybe_make_export_env ();
  1445.  
  1446.       maybe_save_shell_history ();
  1447.  
  1448.       signal (SIGINT, SIG_DFL);
  1449.       signal (SIGQUIT, SIG_DFL);
  1450.       execve (command, args, export_env);
  1451.  
  1452.       adjust_shell_level (1);
  1453.  
  1454.       if (!executable_file (command))
  1455.     builtin_error ("%s: cannot execute: %s", command, strerror (errno));
  1456.       else
  1457.     file_error (command);
  1458.  
  1459.   failed_exec:
  1460.       if (!interactive && !find_variable ("no_exit_on_failed_exec"))
  1461.     exit (EXECUTION_FAILURE);
  1462.       return (EXECUTION_FAILURE);
  1463.     }
  1464. }
  1465.  
  1466. exit_builtin (list)
  1467.      WORD_LIST *list;
  1468. {
  1469.   extern int login_shell;
  1470.  
  1471.   if (interactive)
  1472.     {
  1473.       fprintf (stderr, login_shell ? "logout\n" : "exit\n");
  1474.       fflush (stderr);
  1475.     }
  1476.  
  1477.   exit_or_logout (list);
  1478. #ifdef ibm032
  1479.   return (0);    /* compiler workaround */
  1480. #endif
  1481. }
  1482.  
  1483. /* How to logout. */
  1484. logout_builtin (list)
  1485.      WORD_LIST *list;
  1486. {
  1487.   if (!login_shell && interactive)
  1488.     {
  1489.       builtin_error ("Not login shell: use `exit' or `bye'");
  1490.       return (EXECUTION_FAILURE);
  1491.     }
  1492.   else
  1493.   exit_or_logout (list);
  1494. #ifdef ibm032
  1495.   return (0);    /* compiler workaround */
  1496. #endif
  1497. }
  1498.  
  1499. /* Clean up work for exiting or logging out. */
  1500. Function *last_shell_builtin = (Function *)NULL;
  1501. Function *this_shell_builtin = (Function *)NULL;
  1502.  
  1503. exit_or_logout (list)
  1504.      WORD_LIST *list;
  1505. {
  1506.   extern int last_command_exit_value;
  1507.  
  1508. #if defined (JOB_CONTROL)
  1509.   int exit_immediate_okay;
  1510.  
  1511.   exit_immediate_okay = (!interactive ||
  1512.              last_shell_builtin == exit_builtin ||
  1513.              last_shell_builtin == logout_builtin ||
  1514.              last_shell_builtin == jobs_builtin);
  1515.  
  1516.   /* Check for stopped jobs if the user wants to. */
  1517.   if (!exit_immediate_okay)
  1518.     {
  1519.       register int i;
  1520.       for (i = 0; i < job_slots; i++)
  1521.     if (jobs[i] && (jobs[i]->state == JSTOPPED))
  1522.       {
  1523.         fprintf (stderr, "There are stopped jobs.\n");
  1524.  
  1525.         /* This is NOT superfluous because EOF can get here without
  1526.            going through the command parser.  Set both last and this
  1527.            so that either `exit', `logout', or ^D will work to exit
  1528.            immediately if nothing intervenes. */
  1529.         this_shell_builtin = last_shell_builtin = exit_builtin;
  1530.         return (EXECUTION_FAILURE);
  1531.       }
  1532.     }
  1533. #endif /* JOB_CONTROL */
  1534.  
  1535.   /* Get return value if present.  This means that you can type
  1536.      `logout 5' to a shell, and it returns 5. */
  1537.  
  1538.   if (list)
  1539.     last_command_exit_value = get_numeric_arg (list);
  1540.  
  1541.   /* Run our `~/.bash_logout' file if it exists, and this is a login shell. */
  1542.   if (login_shell)
  1543.     maybe_execute_file ("~/.bash_logout");
  1544.  
  1545.   /* Exit the program. */
  1546.   longjmp (top_level, EXITPROG);
  1547. }
  1548.  
  1549. /* Function called when one of the builtin commands detects a bad
  1550.    option. */
  1551. bad_option (s)
  1552.      char *s;
  1553. {
  1554.   builtin_error ("unknown option: %s", s);
  1555. }
  1556.  
  1557. /* For each variable name in LIST, make that variable appear in the
  1558.    environment passed to simple commands.  If there is no LIST, then
  1559.    print all such variables.  An argument of `-n' says to remove the
  1560.    exported attribute from variables named in LIST. */
  1561. export_builtin (list)
  1562.      register WORD_LIST *list;
  1563. {
  1564.   return (set_or_show_attributes (list, att_exported));
  1565. }
  1566.  
  1567. /* For each variable name in LIST, make that variable have the specified
  1568.    ATTRIBUTE.  An arg of `-n' says to remove the attribute from the the
  1569.    remaining names in LIST.  */
  1570. set_or_show_attributes (list, attribute)
  1571.      register WORD_LIST *list;
  1572.      int attribute;
  1573. {
  1574.   register SHELL_VAR *var;
  1575.   int assign, undo = 0, functions_only = 0;
  1576.   extern int array_needs_making;
  1577.  
  1578.   /* Read arguments from the front of the list. */
  1579.   while (list)
  1580.     {
  1581.       register char *name = list->word->word;
  1582.  
  1583.       if (strcmp (name, "-n") == 0)
  1584.     {
  1585.       undo = 1;
  1586.       list = list->next;
  1587.     }
  1588.       else if (strcmp (name, "-f") == 0)
  1589.     {
  1590.       functions_only = 1;
  1591.       list = list->next;
  1592.     }
  1593.       else if (strcmp (name, "-p") == 0)
  1594.     {
  1595.       list = list->next;
  1596.       continue;
  1597.     }
  1598.       else if (strcmp (name, "--") == 0)
  1599.     {
  1600.       list = list->next;
  1601.       break;
  1602.     }
  1603.       else if (*name == '-')
  1604.     {
  1605.       bad_option (name);
  1606.       return (EXECUTION_FAILURE);
  1607.     }
  1608.       else
  1609.     break;
  1610.     }
  1611.  
  1612.   if (list)
  1613.     {
  1614.       if (attribute & att_exported)
  1615.     array_needs_making = 1;
  1616.  
  1617.       while (list)
  1618.     {
  1619.       register char *name = list->word->word;
  1620.  
  1621.       if (functions_only)
  1622.         {
  1623.           var = find_function (name);
  1624.           if (!var)
  1625.         {
  1626.           builtin_error ("%s: not a function", name);
  1627.         }
  1628.           else
  1629.         {
  1630.           if (undo)
  1631.             var->attributes &= ~attribute;
  1632.           else
  1633.             var->attributes |= attribute;
  1634.         }
  1635.           list = list->next;
  1636.           if (attribute == att_exported)
  1637.         array_needs_making++;
  1638.           continue;
  1639.         }
  1640.  
  1641.       if ((assign = assignment (name)) != 0)
  1642.         {
  1643.           /* Can't call do_assignment(); it will evaluate the RHS of
  1644.          the assignment via a call to expand_string().  The problem
  1645.          is that this has already been expanded once with command
  1646.          and parameter expansion being done.  We call instead a new
  1647.          function do_assignment_no_expand(), which does not do
  1648.          command or parameter substitution. */
  1649.  
  1650.           do_assignment_no_expand (name);
  1651.           name[assign] = '\0';
  1652.         }
  1653.  
  1654.       if (undo)
  1655.         {
  1656.           var = find_variable (name);
  1657.           if (var)
  1658.         var->attributes &= ~attribute;
  1659.         }
  1660.       else
  1661.         {
  1662.           SHELL_VAR *find_tempenv_variable (), *tv;
  1663.  
  1664.           if (tv = find_tempenv_variable (name))
  1665.         {
  1666.           var = bind_variable (tv->name, tv->value);
  1667.           dispose_variable (tv);
  1668.         }
  1669.           else
  1670.         var = find_variable (name);
  1671.  
  1672.           if (!var)
  1673.         {
  1674.           var = bind_variable (name, (char *)NULL);
  1675.           var->attributes |= att_invisible;
  1676.         }
  1677.  
  1678.           var->attributes |= attribute;
  1679.         }
  1680.       array_needs_making++;    /* XXX */
  1681.       list = list->next;
  1682.     }
  1683.     }
  1684.   else
  1685.     {
  1686.       SHELL_VAR **variable_list;
  1687.       register int i;
  1688.  
  1689.       if ((attribute & att_function) || functions_only)
  1690.     {
  1691.       variable_list = all_shell_functions ();
  1692.       if (attribute != att_function)
  1693.         attribute &= ~att_function;    /* so declare -xf works, for example */
  1694.     }
  1695.       else
  1696.     variable_list = all_shell_variables ();
  1697.  
  1698.       if (variable_list)
  1699.     {
  1700.       for (i = 0; var = variable_list[i]; i++)
  1701.         {
  1702.           if ((var->attributes & attribute) && !invisible_p (var))
  1703.         {
  1704.           char flags[6];
  1705.  
  1706.           flags[0] = '\0';
  1707.  
  1708.           if (exported_p (var))
  1709.             strcat (flags, "x");
  1710.  
  1711.           if (readonly_p (var))
  1712.             strcat (flags, "r");
  1713.  
  1714.           if (function_p (var))
  1715.             strcat (flags, "f");
  1716.  
  1717.           if (integer_p (var))
  1718.             strcat (flags, "i");
  1719.  
  1720.           if (flags[0])
  1721.             {
  1722.               printf ("declare -%s ", flags);
  1723.  
  1724.               if (!function_p (var))
  1725.             printf ("%s=%s\n", var->name, value_cell (var));
  1726.               else
  1727.             {
  1728.               char *named_function_string ();
  1729.  
  1730.               printf ("%s\n", named_function_string
  1731.                   (var->name, function_cell (var), 1));
  1732.             }
  1733.             }
  1734.         }
  1735.         }
  1736.       free (variable_list);
  1737.     }
  1738.     }
  1739.   return (EXECUTION_SUCCESS);
  1740. }
  1741.  
  1742. /* Hashing filenames in the shell. */
  1743.  
  1744. #include "hash.h"
  1745.  
  1746. #define FILENAME_HASH_BUCKETS 631
  1747.  
  1748. HASH_TABLE *hashed_filenames;
  1749.  
  1750. typedef struct {
  1751.   /* The full pathname of the file. */
  1752.   char *path;
  1753.  
  1754.   /* Whether `.' appeared before this one in $PATHS. */
  1755.   int check_dot;
  1756. } PATH_DATA;
  1757.  
  1758. #define pathdata(x) ((PATH_DATA *)(x)->data)
  1759.  
  1760. initialize_filename_hashing ()
  1761. {
  1762.   hashed_filenames = make_hash_table (FILENAME_HASH_BUCKETS);
  1763. }
  1764.  
  1765. /* Place FILENAME (key) and FULL_PATHNAME (data->path) into the
  1766.    hash table.    CHECK_DOT if non-null is for future calls to
  1767.    find_hashed_filename (). */
  1768. remember_filename (filename, full_pathname, check_dot)
  1769.      char *filename, *full_pathname;
  1770.      int check_dot;
  1771. {
  1772.   register BUCKET_CONTENTS *item;
  1773.  
  1774.   if (hashing_disabled)
  1775.     return;
  1776.   item = add_hash_item (filename, hashed_filenames);
  1777.   if (item->data)
  1778.     free (pathdata(item)->path);
  1779.   else
  1780.     item->data = (char *)xmalloc (sizeof (PATH_DATA));
  1781.  
  1782.   item->key = savestring (filename);
  1783.   pathdata(item)->path = savestring (full_pathname);
  1784.   pathdata(item)->check_dot = check_dot;
  1785.   item->times_found = 0;
  1786. }
  1787.  
  1788. /* Temporary static. */
  1789. char *dotted_filename = (char *)NULL;
  1790.  
  1791. /* Return the full pathname that FILENAME hashes to.  If FILENAME
  1792.    is hashed, but data->check_dot is non-zero, check ./FILENAME
  1793.    and return that if it is executable. */
  1794. char *
  1795. find_hashed_filename (filename)
  1796.      char *filename;
  1797. {
  1798.   register BUCKET_CONTENTS *item;
  1799.  
  1800.   if (hashing_disabled)
  1801.     return ((char *)NULL);
  1802.  
  1803.   item = find_hash_item (filename, hashed_filenames);
  1804.   if (item) {
  1805.  
  1806.     /* If this filename is hashed, but `.' comes before it in the path,
  1807.        then see if `./filename' is an executable. */
  1808.     if (pathdata(item)->check_dot) {
  1809.  
  1810.       if (dotted_filename)
  1811.     free (dotted_filename);
  1812.  
  1813.       dotted_filename = (char *)xmalloc (3 + strlen (filename));
  1814.       strcpy (dotted_filename, "./");
  1815.       strcat (dotted_filename, filename);
  1816.  
  1817.       if (executable_file (dotted_filename))
  1818.     return (dotted_filename);
  1819.  
  1820.       /* Watch out.  If this file was hashed to "./filename", and
  1821.      "./filename" is not executable, then return NULL. */
  1822.  
  1823.       /* Since we already know "./filename" is not executable, what we're
  1824.      really interested in is whether or not the `path' portion of the
  1825.      hashed filename is equivalent to the current directory, but only if
  1826.      it starts with a `.' (this catches ./. and so on).  same_file ()
  1827.      is in execute_cmd.c; it tests general Unix file equivalence -- same
  1828.      device and inode. */
  1829.  
  1830.       {
  1831.     char *path = pathdata(item)->path;
  1832.  
  1833.     if (*path == '.')
  1834.       {
  1835.         int same = 0;
  1836.         char *rindex(), *tail = rindex (path, '/');
  1837.  
  1838.         if (tail)
  1839.           {
  1840.         *tail = '\0';
  1841.         same = same_file (".", path, (struct stat *)NULL, (struct stat *)NULL);
  1842.         *tail = '/';
  1843.           }
  1844.         if (same)
  1845.           return ((char *)NULL);
  1846.       }
  1847.       }
  1848.     }
  1849.     return (pathdata(item)->path);
  1850.   } else {
  1851.     return ((char *)NULL);
  1852.   }
  1853. }
  1854.  
  1855. /* Print statistics on the current state of hashed commands.  If LIST is
  1856.    not empty, then rehash (or hash in the first place) the specified
  1857.    commands. */
  1858. hash_builtin (list)
  1859.      WORD_LIST *list;
  1860. {
  1861.   extern Function *find_shell_builtin ();
  1862.   extern char *find_user_command ();
  1863.   int any_failed = 0;
  1864.  
  1865.   if (hashing_disabled)
  1866.     {
  1867.       builtin_error ("Hashing is disabled");
  1868.       return (EXECUTION_FAILURE);
  1869.     }
  1870.  
  1871.   if (!list)
  1872.     {
  1873.       /* Print information about current hashed info. */
  1874.       int any_printed = 0;
  1875.       int bucket = 0;
  1876.       register BUCKET_CONTENTS *item_list;
  1877.  
  1878.       while (bucket < hashed_filenames->nbuckets)
  1879.     {
  1880.       item_list = get_hash_bucket (bucket, hashed_filenames);
  1881.       if (item_list)
  1882.         {
  1883.           if (!any_printed)
  1884.         {
  1885.           printf ("hits\tcommand\n");
  1886.           any_printed++;
  1887.         }
  1888.           while (item_list)
  1889.         {
  1890.           printf ("%4d\t%s\n",
  1891.               item_list->times_found, pathdata(item_list)->path);
  1892.           item_list = item_list->next;
  1893.         }
  1894.         }
  1895.       bucket++;
  1896.     }
  1897.       if (!any_printed)
  1898.     {
  1899.       printf ("No commands in hash table.\n");
  1900.     }
  1901.     }
  1902.   else
  1903.     {
  1904.       /* Add or rehash the specified commands. */
  1905.       char *word;
  1906.       char *full_path;
  1907.       SHELL_VAR *var;
  1908.       int any_failed = 0;
  1909.  
  1910.       if (strcmp (list->word->word, "-r") == 0)
  1911.     {
  1912.       int bucket = 0;
  1913.       register BUCKET_CONTENTS *item_list, *prev;
  1914.  
  1915.       while (bucket < hashed_filenames->nbuckets)
  1916.         {
  1917.           item_list = get_hash_bucket (bucket, hashed_filenames);
  1918.           if (item_list)
  1919.         {
  1920.           while (item_list)
  1921.             {
  1922.               prev = item_list;
  1923.               free (item_list->key);
  1924.               free (pathdata(item_list)->path);
  1925.               free (item_list->data);
  1926.               item_list = item_list->next;
  1927.               free (prev);
  1928.             }
  1929.           hashed_filenames->bucket_array[bucket] = (BUCKET_CONTENTS *)NULL;
  1930.         }
  1931.           bucket++;
  1932.         }
  1933.       list = list->next;
  1934.     }
  1935.       else if (*list->word->word == '-')
  1936.     {
  1937.       bad_option (list->word->word);
  1938.       return (EXECUTION_FAILURE);
  1939.     }
  1940.  
  1941.       while (list)
  1942.     {
  1943.       word = list->word->word;
  1944.       if (absolute_program (word))
  1945.         goto next;
  1946.       full_path = find_user_command (word);
  1947.       var = find_function (word);
  1948.  
  1949.       if (!find_shell_builtin (word) && (!var))
  1950.         {
  1951.           if (full_path && executable_file (full_path))
  1952.         {
  1953.           extern int dot_found_in_search;
  1954.           remember_filename (word, full_path, dot_found_in_search);
  1955.         }
  1956.           else
  1957.         {
  1958.           builtin_error ("%s: not found", word);
  1959.           any_failed++;
  1960.         }
  1961.       }
  1962.     next:
  1963.       list = list->next;
  1964.     }
  1965.     }
  1966.   fflush (stdout);
  1967.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  1968. }
  1969. /* History.  Arg of -w FILENAME means write file, arg of -r FILENAME
  1970.    means read file.  Arg of N means only display that many items. */
  1971.  
  1972. history_builtin (list)
  1973.      WORD_LIST *list;
  1974. {
  1975.   register int i;
  1976.   int limited = 0, limit = 0;
  1977.   HIST_ENTRY **hlist;
  1978.  
  1979.   if (list)
  1980.     {
  1981.       char *arg = list->word->word;
  1982.  
  1983.       if ((arg[0] == '-') &&
  1984.       (strlen (arg) == 2) &&
  1985. #if defined (HISTORY_DASH_S)
  1986.       (member (arg[1], "rwans")))
  1987. #else
  1988.       (member (arg[1], "rwan"))) /* -s no longer supported.     Use `fc'. */
  1989. #endif /* !HISTORY_DASH_S */
  1990.     {
  1991.       char *file;
  1992.       int result = EXECUTION_SUCCESS;
  1993.  
  1994.       if (list->next)
  1995.         file = list->next->word->word;
  1996.       else
  1997.         file = get_string_value ("HISTFILE");
  1998.  
  1999.       switch (arg[1])
  2000.         {
  2001.         case 'a':        /* Append `new' lines to file. */
  2002.           {
  2003.         extern int history_lines_this_session;
  2004.         extern int history_lines_in_file;
  2005.  
  2006.         if (history_lines_this_session)
  2007.           {
  2008.             void using_history ();
  2009.  
  2010.             if (history_lines_this_session < where_history ())
  2011.               {
  2012.             /* If the filename was supplied, then create it
  2013.                if it doesn't already exist. */
  2014.             if (file)
  2015.               {
  2016.                 struct stat buf;
  2017.  
  2018.                 if (stat (file, &buf) == -1)
  2019.                   {
  2020.                 int tem;
  2021.  
  2022.                 tem = open (file, O_CREAT, 0666);
  2023.                 close (tem);
  2024.                   }
  2025.               }
  2026.  
  2027.             result =
  2028.               append_history (history_lines_this_session, file);
  2029.             history_lines_in_file += history_lines_this_session;
  2030.             history_lines_this_session = 0;
  2031.               }
  2032.           }
  2033.         break;
  2034.           }
  2035.  
  2036.         case 'w':        /* Write entire history. */
  2037.           {
  2038.         result = write_history (file);
  2039.         break;
  2040.           }
  2041.  
  2042.         case 'r':        /* Read entire file. */
  2043.           {
  2044.         result = read_history (file);
  2045.         break;
  2046.           }
  2047.  
  2048.         case 'n':        /* Read `new' history from file. */
  2049.           {
  2050.         extern int history_lines_in_file;
  2051.  
  2052.         /* Read all of the lines in the file that we haven't
  2053.            already read. */
  2054.         using_history ();
  2055.         result =
  2056.           read_history_range (file, history_lines_in_file, -1);
  2057.         using_history ();
  2058.         history_lines_in_file = where_history ();
  2059.  
  2060.         break;
  2061.           }
  2062.  
  2063. #if defined (HISTORY_DASH_S)
  2064.         case 's':
  2065.           {
  2066.         extern int history_expand ();
  2067.         char *expanded;
  2068.  
  2069.         list = list->next;
  2070.  
  2071.         while (list)
  2072.           {
  2073.             result = history_expand (list->word->word, &expanded);
  2074.             printf ("%s", expanded);
  2075.             fflush (stdout);
  2076.  
  2077.             if (result == -1)
  2078.               return (EXECUTION_FAILURE);
  2079.  
  2080.             free (expanded);
  2081.  
  2082.             list = list->next;
  2083.           }
  2084.         printf ("\n");
  2085.         return (EXECUTION_SUCCESS);
  2086.         break;
  2087.           }
  2088. #endif /* HISTORY_DASH_S */
  2089.         }
  2090.  
  2091.       return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  2092.     }
  2093.       else
  2094.     {
  2095.       if (*list->word->word == '-')
  2096.         {
  2097.           bad_option (list->word->word);
  2098.           return (EXECUTION_FAILURE);
  2099.         }
  2100.  
  2101.       limited = 1;
  2102.       limit = get_numeric_arg (list);
  2103.     }
  2104.     }
  2105.  
  2106.   hlist = history_list ();
  2107.  
  2108.   if (hlist)
  2109.     {
  2110.       for (i = 0;  hlist[i]; i++);
  2111.  
  2112.       if (limit < 0)
  2113.     limit = -limit;
  2114.  
  2115.       if (!limited)
  2116.     i = 0;
  2117.       else
  2118.     if ((i -= limit) < 0)
  2119.       i = 0;
  2120.  
  2121.       while (hlist[i])
  2122.     {
  2123.       QUIT;
  2124.       fprintf (stdout,
  2125.            "%5d%c %s\n", i + history_base, hlist[i]->data ? '*' : ' ',
  2126.            hlist[i]->line);
  2127.       i++;
  2128.     }
  2129.     }
  2130.   return (EXECUTION_SUCCESS);
  2131. }
  2132.  
  2133. /* Non-zero means that pwd always give verbatim directory, regardless of
  2134.    symbolic link following. */
  2135. int verbatim_pwd = 1;
  2136.  
  2137. /* Print the name of the current working directory. */
  2138. pwd_builtin (list)
  2139.      WORD_LIST *list;
  2140. {
  2141.   char *get_working_directory (), *getwd (), *directory;
  2142.  
  2143.   no_args (list);
  2144.  
  2145.   if (verbatim_pwd)
  2146.     {
  2147.       char *buffer = (char *)xmalloc (MAXPATHLEN);
  2148.       directory = getwd (buffer);
  2149.  
  2150.       if (!directory)
  2151.     {
  2152.       builtin_error ("%s", buffer);
  2153.       free (buffer);
  2154.     }
  2155.     }
  2156.   else
  2157.     {
  2158.       directory = get_working_directory ("pwd");
  2159.     }
  2160.  
  2161.   if (directory)
  2162.     {
  2163.       printf ("%s\n", directory);
  2164.       fflush (stdout);
  2165.       free (directory);
  2166.       return (EXECUTION_SUCCESS);
  2167.     }
  2168.   else
  2169.     return (EXECUTION_FAILURE);
  2170. }
  2171.  
  2172. /* Read the value of the shell variables whose names follow.
  2173.    The reading is done from the current input stream, whatever
  2174.    that may be.     Successive words of the input line are assigned
  2175.    to the variables mentioned in LIST.    The last variable in LIST
  2176.    gets the remainder of the words on the line.     If no variables
  2177.    are mentioned in LIST, then the default variable is $REPLY.
  2178.  
  2179.    S. R. Bourne's shell complains if you don't name a variable
  2180.    to receive the stuff that is read.  GNU's shell doesn't.  This
  2181.    allows you to let the user type random things. */
  2182. read_builtin (list)
  2183.      WORD_LIST *list;
  2184. {
  2185.   extern int interrupt_immediately, free ();
  2186.   register char *varname;
  2187.   int size, c, i = 0, fildes, ignore_backslash_nl = 1;
  2188.   char *input_string, *ifs_chars;
  2189.   WORD_LIST *words, *rwords, *list_string ();
  2190.   extern char *string_list_dollar_star ();
  2191.   FILE *input_stream;
  2192.  
  2193.   ifs_chars = get_string_value ("IFS");
  2194.   input_string = (char *)xmalloc (size = 128);
  2195.  
  2196.   /* We need unbuffered input from stdin.  So we make a new
  2197.      unbuffered stream with the same file descriptor, then
  2198.      unbuffer that one. */
  2199.   fildes = dup (fileno (stdin));
  2200.  
  2201.   if (fildes == -1)
  2202.     return (EXECUTION_FAILURE);
  2203.  
  2204.   input_stream = fdopen (fildes, "r");
  2205.  
  2206.   if (!input_stream)
  2207.     {
  2208.       close (fildes);
  2209.       return (EXECUTION_FAILURE);
  2210.     }
  2211.  
  2212.   setbuf (input_stream, (char *)NULL);
  2213.  
  2214.   {
  2215.     int stream_close ();
  2216.     begin_unwind_frame ("read_builtin");
  2217.     add_unwind_protect (free, input_string);
  2218.     add_unwind_protect (stream_close, input_stream);
  2219.     interrupt_immediately++;
  2220.   }
  2221.  
  2222.   if (list)
  2223.     {
  2224.       if (strcmp (list->word->word, "-r") == 0)
  2225.     {
  2226.       ignore_backslash_nl = 0;
  2227.       list = list->next;
  2228.     }
  2229.       else if (*list->word->word == '-')
  2230.     {
  2231.       bad_option (list->word->word);
  2232.       return (EXECUTION_FAILURE);
  2233.     }
  2234.     }
  2235.  
  2236.   while ((c = getc (input_stream)) != EOF)
  2237.     {
  2238.       if (i + 1 >= size)
  2239.     input_string = (char *)xrealloc (input_string, size += 128);
  2240.  
  2241.       input_string[i++] = c;
  2242.  
  2243.       if (c == '\n')
  2244.     {
  2245.       if (ignore_backslash_nl &&
  2246.           (i >= 2) && (input_string[i - 2] == '\\'))
  2247.         {
  2248.           i -= 2;
  2249.           continue;
  2250.         }
  2251.       /* Both ksh and the s5r3 sh chop off the trailing newline. */
  2252.       i--;
  2253.       break;
  2254.     }
  2255.     }
  2256.   input_string[i] = '\0';
  2257.  
  2258.   interrupt_immediately--;
  2259.   discard_unwind_frame ("read_builtin");
  2260.  
  2261.   fclose (input_stream);
  2262.  
  2263.   if (c == EOF)
  2264.     return (EXECUTION_FAILURE);
  2265.  
  2266.   if (!list)
  2267.     {
  2268.       bind_variable ("REPLY", input_string);
  2269.       free (input_string);
  2270.     }
  2271.   else
  2272.     {
  2273.       words = list_string (input_string, ifs_chars, 0);
  2274.       rwords = words;
  2275.  
  2276.       free (input_string);
  2277.  
  2278.       while (list)
  2279.     {
  2280.       varname = list->word->word;
  2281.  
  2282.       if (!list->next)
  2283.         {
  2284.           /* Call string_list_dollar_star because P1003.2.9 specifies
  2285.          that the intervening separators are preserved in the
  2286.          result of a read that specifies fewer variables than words
  2287.          read. */
  2288.           char *t = words ? string_list_dollar_star (words) : "";
  2289.           bind_variable (varname, t);
  2290.         }
  2291.       else
  2292.         bind_variable (varname, words ? words->word->word : "");
  2293.       stupidly_hack_special_variables (varname);
  2294.       list = list->next;
  2295.       if (words)
  2296.         words = words->next;
  2297.     }
  2298.       if (rwords)
  2299.     dispose_words (rwords);
  2300.     }
  2301.  
  2302.   return (EXECUTION_SUCCESS);
  2303. }
  2304.  
  2305. /* This way I don't have to know whether fclose is a function or a macro. */
  2306. int
  2307. stream_close (file)
  2308.      FILE *file;
  2309. {
  2310.   return (fclose (file));
  2311. }
  2312.  
  2313. /* For each variable name in LIST, make that variable read_only. */
  2314. readonly_builtin (list)
  2315.      register WORD_LIST *list;
  2316. {
  2317.   return (set_or_show_attributes (list, att_readonly));
  2318. }
  2319.  
  2320. /* If we are executing a user-defined function then exit with the value
  2321.    specified as an argument.  if no argument is given, then the last
  2322.    exit status is used. */
  2323. return_builtin (list)
  2324.      WORD_LIST *list;
  2325. {
  2326.   extern int last_command_exit_value;
  2327.   extern int return_catch_flag, return_catch_value;
  2328.   extern jmp_buf return_catch;
  2329.  
  2330.   return_catch_value = get_numeric_arg (list);
  2331.  
  2332.   if (!list)
  2333.     return_catch_value = last_command_exit_value;
  2334.  
  2335.   if (return_catch_flag)
  2336.     longjmp (return_catch, 1);
  2337.   else
  2338.     {
  2339.       builtin_error ("Can only `return' from a function");
  2340.       return (EXECUTION_FAILURE);    /* XXX was longjmp(top_level, ...) */
  2341.     }
  2342. }
  2343.  
  2344. /* An a-list used to match long options for set -o to the corresponding
  2345.    option letter. */
  2346. struct {
  2347.   char *name;
  2348.   int letter;
  2349. } o_options[] = {
  2350.   {"allexport",    'a'},
  2351.   {"errexit",    'e'},
  2352.   {"histexpand",'H'},
  2353.   {"monitor",    'm'},
  2354.   {"noexec",    'n'},
  2355.   {"noglob",    'f'},
  2356.   {"nohash",    'd'},
  2357.   {"nounset",    'u'},
  2358.   {"verbose",    'v'},
  2359.   {"xtrace",    'x'},
  2360.   {(char *)NULL, 0},
  2361. };
  2362.  
  2363. static void
  2364. list_long_opts ()
  2365. {
  2366.   register int    i;
  2367.   char *on = "on", *off = "off";
  2368.   extern int noclobber, no_brace_expansion;
  2369.   extern SHELL_VAR *find_variable ();
  2370.  
  2371. #if defined (JOB_CONTROL)
  2372.   extern int asynchronous_notification;
  2373. #endif /* JOB_CONTROL */
  2374.  
  2375. #if defined (READLINE)
  2376.   extern int rl_editing_mode, no_line_editing;
  2377. #endif /* READLINE */
  2378.  
  2379.   printf ("%-15s\t%s\n", "braceexpand", (no_brace_expansion == 0) ? on : off);
  2380.   printf ("%-15s\t%s\n", "noclobber", (noclobber == 1) ? on : off);
  2381.  
  2382. #if defined (JOB_CONTROL)
  2383.   printf ("%-15s\t%s\n", "notify",
  2384.       (asynchronous_notification == 1) ? on : off);
  2385. #endif /* JOB_CONTROL */
  2386.  
  2387.   if (find_variable ("ignoreeof") || find_variable ("IGNOREEOF"))
  2388.     printf ("%-15s\t%s\n", "ignoreeof", on);
  2389.   else
  2390.     printf ("%-15s\t%s\n", "ignoreeof", off);
  2391.  
  2392. #if defined (READLINE)
  2393.   if (no_line_editing)
  2394.     {
  2395.       printf ("%-15s\toff\n", "emacs");
  2396.       printf ("%-15s\toff\n", "vi");
  2397.     }
  2398.   else
  2399.     {
  2400.       /* Magic.     This code `knows' how readline handles rl_editing_mode. */
  2401.       printf ("%-15s\t%s\n", "emacs", (rl_editing_mode == 1) ? on : off);
  2402.       printf ("%-15s\t%s\n", "vi", (rl_editing_mode == 0) ? on : off);
  2403.     }
  2404. #endif /* READLINE */
  2405.  
  2406.   for (i = 0; o_options[i].name; i++)
  2407.     {
  2408.       char name[2];
  2409.       int *on_or_off, zero = 0;
  2410.       extern int *find_flag ();
  2411.  
  2412.       name[0] = o_options[i].letter;
  2413.       name[1] = '\0';
  2414.       on_or_off = find_flag (name);
  2415.       if (on_or_off == (int *)FLAG_ERROR)
  2416.     on_or_off = &zero;
  2417.       printf ("%-15s\t%s\n", o_options[i].name, (*on_or_off == 1) ? on : off);
  2418.     }
  2419. }
  2420.  
  2421. set_minus_o_option (on_or_off, option_name)
  2422.      int on_or_off;
  2423.      char *option_name;
  2424. {
  2425.   extern int no_brace_expansion;
  2426.   int option_char = -1;
  2427.  
  2428.   if (strcmp (option_name, "braceexpand") == 0)
  2429.     {
  2430.       if (on_or_off == FLAG_ON)
  2431.     no_brace_expansion = 0;
  2432.       else
  2433.     no_brace_expansion = 1;
  2434.     }
  2435.   else if (strcmp (option_name, "noclobber") == 0)
  2436.     {
  2437.       if (on_or_off == FLAG_ON)
  2438.     bind_variable ("noclobber", "");
  2439.       else
  2440.     unbind_variable ("noclobber");
  2441.       stupidly_hack_special_variables ("noclobber");
  2442.     }
  2443. #if defined (JOB_CONTROL)
  2444.   else if (strcmp (option_name, "notify") == 0)
  2445.     {
  2446.       if (on_or_off == FLAG_ON)
  2447.     bind_variable ("notify", "");
  2448.       else
  2449.     unbind_variable ("notify");
  2450.       stupidly_hack_special_variables ("notify");
  2451.     }
  2452. #endif /* JOB_CONTROL */
  2453.  
  2454.   else if (strcmp (option_name, "ignoreeof") == 0)
  2455.     {
  2456.       unbind_variable ("ignoreeof");
  2457.       unbind_variable ("IGNOREEOF");
  2458.       if (on_or_off == FLAG_ON)
  2459.     bind_variable ("IGNOREEOF", "10");
  2460.       stupidly_hack_special_variables ("IGNOREEOF");
  2461.     }
  2462.  
  2463. #if defined (READLINE)
  2464.   else if ((strcmp (option_name, "emacs") == 0) ||
  2465.        (strcmp (option_name, "vi") == 0))
  2466.     {
  2467.       extern int no_line_editing;
  2468.  
  2469.       if (on_or_off == FLAG_ON)
  2470.     {
  2471.       rl_variable_bind ("editing-mode", option_name);
  2472.  
  2473.       if (interactive)
  2474.         with_input_from_stdin ();
  2475.       no_line_editing = 0;
  2476.     }
  2477.       else
  2478.     {
  2479.       extern int rl_editing_mode;
  2480.       int isemacs = (rl_editing_mode == 1);
  2481.       if ((isemacs && strcmp (option_name, "emacs") == 0) ||
  2482.           (!isemacs && strcmp (option_name, "vi") == 0))
  2483.         {
  2484.           if (interactive)
  2485.         with_input_from_stream (stdin, "stdin");
  2486.           no_line_editing = 1;
  2487.         }
  2488.       else
  2489.         builtin_error ("not in %s editing mode",
  2490.                 option_name);
  2491.     }
  2492.     }
  2493. #endif /* READLINE */
  2494.   else
  2495.     {
  2496.       register int i;
  2497.       for (i = 0; o_options[i].name; i++)
  2498.     {
  2499.       if (strcmp (option_name, o_options[i].name) == 0)
  2500.         {
  2501.           option_char = o_options[i].letter;
  2502.           break;
  2503.         }
  2504.     }
  2505.       if (option_char == -1)
  2506.     {
  2507.       builtin_error ("%s: unknown option name", option_name);
  2508.       return (EXECUTION_FAILURE);
  2509.     }
  2510.       if (change_flag_char (option_char, on_or_off) == FLAG_ERROR)
  2511.     {
  2512.       bad_option (option_name);
  2513.       return (EXECUTION_FAILURE);
  2514.     }
  2515.     }
  2516.     return (EXECUTION_SUCCESS);
  2517. }
  2518.  
  2519. /* Set some flags from the word values in the input list.  If LIST is empty,
  2520.    then print out the values of the variables instead.    If LIST contains
  2521.    non-flags, then set $1 - $9 to the successive words of LIST. */
  2522. set_builtin (list)
  2523.      WORD_LIST *list;
  2524. {
  2525.   int on_or_off, flag_name, force_assignment = 0;
  2526.  
  2527.   if (!list)
  2528.     {
  2529.       SHELL_VAR **vars;
  2530.  
  2531.       vars = all_shell_variables ();
  2532.       if (vars)
  2533.     {
  2534.       print_var_list (vars);
  2535.       free (vars);
  2536.     }
  2537.  
  2538.       vars = all_shell_functions ();
  2539.       if (vars)
  2540.     {
  2541.       print_var_list (vars);
  2542.       free (vars);
  2543.     }
  2544.  
  2545.       return (EXECUTION_SUCCESS);
  2546.     }
  2547.  
  2548. #if defined (NOTDEF)
  2549.   if (strcmp (list->word->word, "+") == 0 && list->next == NULL)
  2550.     {
  2551.       SHELL_VAR **vars;
  2552.  
  2553.       vars = all_shell_variables ();
  2554.       if (vars)
  2555.     {
  2556.       print_vars_no_values (vars);
  2557.       free (vars);
  2558.     }
  2559.       return (EXECUTION_SUCCESS);
  2560.     }
  2561. #endif /* NOTDEF */
  2562.  
  2563.   /* Do the set command.  While the list consists of words starting with
  2564.      '-' or '+' treat them as flags, otherwise, start assigning them to
  2565.      $1 ... $n. */
  2566.   while (list)
  2567.     {
  2568.       char *string = list->word->word;
  2569.  
  2570.       /* If the argument is `--' then signal the end of the list and
  2571.      remember the remaining arguments. */
  2572.       if ((strcmp (string, "--") == 0) || (strcmp (string, "-") == 0))
  2573.     {
  2574.       list = list->next;
  2575.       if (strcmp (string, "--") == 0)
  2576.         force_assignment = 1;    /* so set -- unsets pos params */
  2577.       break;
  2578.     }
  2579.  
  2580.       if ((on_or_off = *string) &&
  2581.       (on_or_off == '-' || on_or_off == '+'))
  2582.     {
  2583.       int i = 1;
  2584.       while (flag_name = string[i++])
  2585.         {
  2586.           if (flag_name == '?')
  2587.         {
  2588.           /* Print all the possible flags. */
  2589.         }
  2590.           else if (flag_name == 'o')    /* -+o option-name */
  2591.         {
  2592.           char *option_name;
  2593.           WORD_LIST *opt;
  2594.  
  2595.           opt = list->next;
  2596.           if (!opt)
  2597.             {
  2598.               list_long_opts ();
  2599.               continue;
  2600.             }
  2601.           option_name = opt->word->word;
  2602.           if (!option_name || !*option_name || (*option_name == '-'))
  2603.             {
  2604.               list_long_opts ();
  2605.               continue;
  2606.             }
  2607.           list = list->next;    /* skip over option name */
  2608.  
  2609.           if (set_minus_o_option (on_or_off, option_name) != EXECUTION_SUCCESS)
  2610.             return (EXECUTION_FAILURE);
  2611.         }
  2612.  
  2613.           else
  2614.         {
  2615.           if (change_flag_char (flag_name, on_or_off) == FLAG_ERROR)
  2616.             {
  2617.               char opt[3];
  2618.               opt[0] = on_or_off;
  2619.               opt[1] = flag_name;
  2620.               opt[2] = '\0';
  2621.               bad_option (opt);
  2622.               return (EXECUTION_FAILURE);
  2623.             }
  2624.         }
  2625.         }
  2626.     }
  2627.       else
  2628.     {
  2629.       break;
  2630.     }
  2631.       list = list->next;
  2632.     }
  2633.  
  2634.   /* Assigning $1 ... $n */
  2635.   if (list || force_assignment)
  2636.     remember_args (list, 1);
  2637.   return (EXECUTION_SUCCESS);
  2638. }
  2639.  
  2640. /* **************************************************************** */
  2641. /*                                    */
  2642. /*            Pushing and Popping a Context            */
  2643. /*                                    */
  2644. /* **************************************************************** */
  2645.  
  2646. WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
  2647. int dollar_arg_stack_slots = 0;
  2648. int dollar_arg_stack_index = 0;
  2649.  
  2650. push_context ()
  2651. {
  2652.   extern int variable_context;
  2653.  
  2654.   push_dollar_vars ();
  2655.   variable_context++;
  2656. }
  2657.  
  2658. pop_context ()
  2659. {
  2660.   extern int variable_context;
  2661.  
  2662.   pop_dollar_vars ();
  2663.   kill_all_local_variables ();
  2664.   variable_context--;
  2665. }
  2666.  
  2667. /* Save the existing arguments on a stack. */
  2668. push_dollar_vars ()
  2669. {
  2670.   extern WORD_LIST *list_rest_of_args ();
  2671.  
  2672.   if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
  2673.     {
  2674.       if (!dollar_arg_stack)
  2675.     {
  2676.       dollar_arg_stack =
  2677.         (WORD_LIST **)xrealloc (dollar_arg_stack,
  2678.                     (dollar_arg_stack_slots += 10)
  2679.                     * sizeof (WORD_LIST **));
  2680.     }
  2681.     }
  2682.   dollar_arg_stack[dollar_arg_stack_index] = list_rest_of_args ();
  2683.   dollar_arg_stack[++dollar_arg_stack_index] = (WORD_LIST *)NULL;
  2684. }
  2685.  
  2686. pop_dollar_vars ()
  2687. {
  2688.   if (!dollar_arg_stack || !dollar_arg_stack_index)
  2689.     return;
  2690.  
  2691.   remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
  2692.   dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
  2693.   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
  2694. }
  2695.  
  2696. /* Remember LIST in $0 ... $9, and REST_OF_ARGS.  If DESTRUCTIVE is
  2697.    non-zero, then discard whatever the existing arguments are, else
  2698.    only discard the ones that are to be replaced. */
  2699. remember_args (list, destructive)
  2700.      WORD_LIST *list;
  2701.      int destructive;
  2702. {
  2703.   register int i;
  2704.   extern WORD_LIST *copy_word_list ();
  2705.  
  2706.   for (i = 1; i < 10; i++)
  2707.     {
  2708.       if (destructive && dollar_vars[i])
  2709.     {
  2710.       free (dollar_vars[i]);
  2711.       dollar_vars[i] = (char *)NULL;
  2712.     }
  2713.  
  2714.       if (list)
  2715.     {
  2716.       if (!destructive && dollar_vars[i])
  2717.         free (dollar_vars[i]);
  2718.  
  2719.       dollar_vars[i] = savestring (list->word->word);
  2720.       list = list->next;
  2721.     }
  2722.     }
  2723.  
  2724.   /* If arguments remain, assign them to REST_OF_ARGS. */
  2725.   if (!list)
  2726.     {
  2727.       dispose_words (rest_of_args);
  2728.       rest_of_args = NULL;
  2729.     }
  2730.   else
  2731.     {
  2732.       rest_of_args = (WORD_LIST *)copy_word_list (list);
  2733.     }
  2734. }
  2735.  
  2736. /* Return if LIST is NULL else barf and jump to top_level. */
  2737. no_args (list)
  2738.      WORD_LIST *list;
  2739. {
  2740.   if (list)
  2741.     {
  2742.       builtin_error ("extra arguments");
  2743.       longjmp (top_level, DISCARD);
  2744.     }
  2745. }
  2746.  
  2747. /* Shift the arguments ``left''.  Shift DOLLAR_VARS down then take one
  2748.    off of REST_OF_ARGS and place it into DOLLAR_VARS[9].  If LIST has
  2749.    anything in it, it is a number which says where to start the shifting. */
  2750. shift_builtin (list)
  2751.      WORD_LIST *list;
  2752. {
  2753.   int times = get_numeric_arg (list);
  2754.   int number, r;
  2755.   WORD_LIST *args;
  2756.   extern WORD_LIST *list_rest_of_args ();
  2757.  
  2758.   if (times <= 0)
  2759.     {
  2760.       builtin_error ("shift count must be >= 1");
  2761.       return (EXECUTION_FAILURE);
  2762.     }
  2763.  
  2764.   args = list_rest_of_args ();
  2765.   number = list_length (args);
  2766.   dispose_words (args);
  2767.  
  2768.   r = EXECUTION_SUCCESS;
  2769.  
  2770.   if (times > number)
  2771.     {
  2772.       times = number;
  2773.       r = EXECUTION_FAILURE;
  2774.     }
  2775.  
  2776.   while (times-- > 0)
  2777.     {
  2778.       register int count;
  2779.  
  2780.       if (dollar_vars[1])
  2781.     free (dollar_vars[1]);
  2782.  
  2783.       for (count = 1; count < 9; count++)
  2784.     dollar_vars[count] = dollar_vars[count + 1];
  2785.  
  2786.       if (rest_of_args)
  2787.     {
  2788.       WORD_LIST *temp = rest_of_args;
  2789.  
  2790.       dollar_vars[9] = savestring (temp->word->word);
  2791.       rest_of_args = rest_of_args->next;
  2792.       dispose_word (temp->word);
  2793.     }
  2794.       else
  2795.     dollar_vars[9] = (char *)NULL;
  2796.     }
  2797.   /* according to POSIX.2, should return > 0 if `times' > $#, otherwise 0 */
  2798.   return (r);
  2799. }
  2800.  
  2801. /* TEST/[ builtin. */
  2802. test_builtin (list)
  2803.      WORD_LIST *list;
  2804. {
  2805.   char **argv;
  2806.   int argc, result;
  2807.   WORD_LIST *t = list;
  2808.  
  2809.   /* We let Matthew Bradburn and Kevin Braunsdorf's code do the
  2810.      actual test command.  So turn the list of args into an array
  2811.      of strings, since that is what his code wants. */
  2812.  
  2813.   if (!list)
  2814.     {
  2815.       if (strcmp (this_command_name, "[") == 0)
  2816.     builtin_error ("missing `]'");
  2817.  
  2818.       return (EXECUTION_FAILURE);
  2819.     }
  2820.  
  2821.   /* Get the length of the argument list. */
  2822.   for (argc = 0; t; t = t->next, argc++);
  2823.  
  2824.   /* Account for argv[0] being a command name.    This makes our life easier. */
  2825.   argc++;
  2826.   argv = (char **)xmalloc ((1 + argc) * sizeof (char *));
  2827.   argv[argc] = (char *)NULL;
  2828.  
  2829.   /* this_command_name is the name of the command that invoked this
  2830.      function.    So you can't call test_builtin () directly from
  2831.      within this code, there are too many things to worry about. */
  2832.   argv[0] = savestring (this_command_name);
  2833.  
  2834.   for (t = list, argc = 1; t; t = t->next, argc++)
  2835.     argv[argc] = savestring (t->word->word);
  2836.  
  2837.   result = test_command (argc, argv);
  2838.   free_array (argv);
  2839.   return (result);
  2840. }
  2841.  
  2842. /* Print the totals for system and user time used.  The
  2843.    information comes from variables in jobs.c used to keep
  2844.    track of this stuff. */
  2845.  
  2846. times_builtin (list)
  2847.      WORD_LIST *list;
  2848. {
  2849. #if defined (HAVE_RESOURCE) && !defined (hpux)
  2850.   struct rusage self, kids;
  2851.  
  2852.   no_args (list);
  2853.  
  2854.   getrusage (RUSAGE_SELF, &self);
  2855.   getrusage (RUSAGE_CHILDREN, &kids);    /* terminated child processes */
  2856.  
  2857.   print_timeval (&self.ru_utime);
  2858.   putchar (' ');
  2859.   print_timeval (&self.ru_stime);
  2860.   putchar ('\n');
  2861.   print_timeval (&kids.ru_utime);
  2862.   putchar (' ');
  2863.   print_timeval (&kids.ru_stime);
  2864.   putchar ('\n');
  2865.  
  2866. #else /* !HAVE_RESOURCE || !hpux */
  2867. #if !defined (BrainDeath)
  2868.   struct tms t;
  2869.  
  2870.   no_args (list);
  2871.  
  2872.   times (&t);
  2873.  
  2874.   /* As of System V.3, HP-UX 6.5, and other ATT-like systems, this stuff is
  2875.      returned in terms of clock ticks (HZ from sys/param.h).  C'mon, guys.
  2876.      This kind of stupid clock-dependent stuff is exactly the reason 4.2BSD
  2877.      introduced the `timeval' struct. */
  2878.  
  2879.   print_time_in_hz (t.tms_utime);
  2880.   putchar (' ');
  2881.   print_time_in_hz (t.tms_stime);
  2882.   putchar ('\n');
  2883.   print_time_in_hz (t.tms_cutime);
  2884.   putchar (' ');
  2885.   print_time_in_hz (t.tms_cstime);
  2886.   putchar ('\n');
  2887. #endif /* BrainDeath */
  2888. #endif /* !HAVE_RESOURCE || !hpux  */
  2889.  
  2890.   return (EXECUTION_SUCCESS);
  2891. }
  2892.  
  2893. /* The trap command:
  2894.  
  2895.    trap <arg> <signal ...>
  2896.    trap <signal ...>
  2897.    trap -l
  2898.    trap
  2899.  
  2900.    Set things up so that ARG is executed when SIGNAL(s) N is recieved.
  2901.    If ARG is the empty string, then ignore the SIGNAL(s).  If there is
  2902.    no ARG, then set the trap for SIGNAL(s) to its original value.  Just
  2903.    plain "trap" means to print out the list of commands associated with
  2904.    each signal number.    Single arg of "-l" means list the signal names. */
  2905.  
  2906. /* Possible operations to perform on the list of signals.*/
  2907. #define SET 0            /* Set this signal to first_arg. */
  2908. #define REVERT 1        /* Revert to this signals original value. */
  2909. #define IGNORE 2        /* Ignore this signal. */
  2910.  
  2911. trap_builtin (list)
  2912.      WORD_LIST *list;
  2913. {
  2914.   register int i;
  2915.  
  2916.   if (!list)
  2917.     {
  2918.       for (i = 0; i < NSIG; i++)
  2919.     if (trap_list[i] != (char *)DEFAULT_SIG)
  2920.       printf ("trap '%s' %s\n",
  2921.           (trap_list[i] == (char *)IGNORE_SIG) ? "" : trap_list[i],
  2922.           signal_name (i));
  2923.       return (EXECUTION_SUCCESS);
  2924.     }
  2925.   else
  2926.     {
  2927.       char *first_arg = list->word->word;
  2928.       int operation = SET, any_failed = 0;
  2929.  
  2930.       if (strcmp ("-l", first_arg) == 0)
  2931.     {
  2932.       int column = 0;
  2933.  
  2934.       for (i = 0; i < NSIG; i++)
  2935.         {
  2936.           printf ("%2d) %s", i, signal_name (i));
  2937.           if (++column < 4)
  2938.         printf ("\t");
  2939.           else
  2940.         {
  2941.           printf ("\n");
  2942.           column = 0;
  2943.         }
  2944.         }
  2945.       if (column != 0)
  2946.         printf ("\n");
  2947.       return (EXECUTION_SUCCESS);
  2948.     }
  2949.  
  2950.       if (signal_object_p (first_arg))
  2951.     operation = REVERT;
  2952.       else
  2953.     {
  2954.       list = list->next;
  2955.       if (*first_arg == '\0')
  2956.         operation = IGNORE;
  2957.       else if (strcmp (first_arg, "-") == 0)
  2958.         operation = REVERT;
  2959.     }
  2960.  
  2961.       while (list)
  2962.     {
  2963.       int signal = decode_signal (list->word->word);
  2964.  
  2965.       if (signal == NO_SIG)
  2966.         {
  2967.           builtin_error ("%s: not a signal specification", list->word->word);
  2968.           any_failed++;
  2969.         }
  2970.       else
  2971.         {
  2972.           switch (operation)
  2973.         {
  2974.           case SET:
  2975.             set_signal (signal, first_arg);
  2976.             break;
  2977.  
  2978.           case REVERT:
  2979.             restore_default_signal (signal);
  2980.             break;
  2981.  
  2982.           case IGNORE:
  2983.             ignore_signal (signal);
  2984.             break;
  2985.         }
  2986.         }
  2987.       list = list->next;
  2988.     }
  2989.       return ((!any_failed) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
  2990.     }
  2991. }
  2992.  
  2993. /* For each word in LIST, find out what the shell is going to do with
  2994.    it as a simple command. i.e., which file would this shell use to
  2995.    execve, or if it is a builtin command, or an alias.    Possible flag
  2996.    arguments:
  2997.     -type        Returns the "type" of the object, one of
  2998.             `alias', `function', `builtin', or `file'.
  2999.  
  3000.     -path        Returns the pathname of the file if -type is
  3001.             a file.
  3002.  
  3003.     -all        Returns all occurrences of words, whether they
  3004.             be a filename in the path, alias, function,
  3005.             or builtin.
  3006.    Order of evaluation:
  3007.     alias
  3008.     keyword
  3009.     function
  3010.     builtin
  3011.     file
  3012.  */
  3013. type_builtin (list)
  3014.      WORD_LIST *list;
  3015. {
  3016.   extern Function *find_shell_builtin ();
  3017.   extern char *user_command_matches (), *find_user_command ();
  3018.   extern struct { char *word; int token; } token_word_alist[];
  3019.   int path_only, type_only, all;
  3020.   int found_something = 0;
  3021.   int found_file = 0;
  3022.  
  3023.   path_only = type_only = all = 0;
  3024.  
  3025.   while (list && *(list->word->word) == '-')
  3026.     {
  3027.       char *flag = &(list->word->word[1]);
  3028.  
  3029.       if ((strcmp (flag, "type") == 0) || (strcmp (flag, "t") == 0))
  3030.     {
  3031.       type_only = 1;
  3032.       path_only = 0;
  3033.     }
  3034.       else if ((strcmp (flag, "path") == 0) || (strcmp (flag, "p") == 0))
  3035.     {
  3036.       path_only = 1;
  3037.       type_only = 0;
  3038.     }
  3039.       else if ((strcmp (flag, "all") == 0) || (strcmp (flag, "a") == 0))
  3040.     {
  3041.       all = 1;
  3042.     }
  3043.       else
  3044.     {
  3045.       bad_option (flag);
  3046.       return (EXECUTION_FAILURE);
  3047.     }
  3048.       list = list->next;
  3049.     }
  3050.  
  3051.   while (list)
  3052.     {
  3053.       char *command = list->word->word;
  3054.       SHELL_VAR *func = find_function (command);
  3055.       int hashed = 0;
  3056.       char *full_path = (char *)NULL;
  3057.  
  3058. #ifdef ALIAS
  3059.       /* Command is an alias? */
  3060.       ASSOC *alias = find_alias (command);
  3061.       if (alias)
  3062.     {
  3063.       if (type_only)
  3064.         printf ("alias\n");
  3065.       else if (!path_only)
  3066.         printf ("%s is aliased to `%s'\n", command, alias->value);
  3067.       found_something++;
  3068.       if (!all)
  3069.         goto next_item;
  3070.     }
  3071. #endif
  3072.  
  3073.       /* Command is a shell reserved word? */
  3074.       {
  3075.     register int i;
  3076.  
  3077.     for (i = 0; token_word_alist[i].word; i++)
  3078.       {
  3079.         if (strcmp (token_word_alist[i].word, command) == 0)
  3080.           {
  3081.         if (type_only)
  3082.           printf ("reserved word\n");
  3083.         else if (!path_only)
  3084.           printf ("%s is a shell reserved word\n", command);
  3085.  
  3086.         found_something++;
  3087.  
  3088.         if (!all)
  3089.           goto next_item;
  3090.  
  3091.         break;
  3092.           }
  3093.       }
  3094.       }
  3095.  
  3096.       /* Command is a function? */
  3097.       if (func)
  3098.     {
  3099.       if (type_only)
  3100.         printf ("function\n");
  3101.       else if (!path_only)
  3102.         {
  3103. #define PRETTY_PRINT_FUNC    1
  3104.           extern char *named_function_string ();
  3105.           char *result;
  3106.  
  3107.           printf ("%s is a function\n", command);
  3108.  
  3109.           /* We're blowing away the_printed_command here... */
  3110.  
  3111.           result = named_function_string (command,
  3112.                     (COMMAND *) function_cell (func),
  3113.                     PRETTY_PRINT_FUNC);
  3114.           printf ("%s\n", result);
  3115. #undef PRETTY_PRINT_FUNC
  3116.         }
  3117.       found_something++;
  3118.       if (!all)
  3119.         goto next_item;
  3120.     }
  3121.  
  3122.       /* Command is a builtin? */
  3123.       if (find_shell_builtin (command))
  3124.     {
  3125.       if (type_only)
  3126.         printf ("builtin\n");
  3127.       else if (!path_only)
  3128.         printf ("%s is a shell builtin\n", command);
  3129.       found_something++;
  3130.       if (!all)
  3131.         goto next_item;
  3132.     }
  3133.  
  3134.       full_path = (char *)NULL;
  3135.  
  3136.       /* Command is a disk file? */
  3137.       if (absolute_program (command))
  3138.     {
  3139.       full_path = savestring (command);
  3140.     }
  3141.       else
  3142.     {
  3143.       /* If the user isn't doing "-all", then we might care about
  3144.          whether the file is present in our hash table. */
  3145.       if (!all)
  3146.         {
  3147.           if ((full_path = find_hashed_filename (command)) != (char *)NULL)
  3148.         {
  3149.           hashed++;
  3150.           full_path = savestring (full_path);
  3151.         }
  3152.           else
  3153.         {
  3154.           full_path = find_user_command (command);
  3155.         }
  3156.         }
  3157.     }
  3158.       if (all)
  3159.     {
  3160.       /* If full_path was set then it is an absolute path name. */
  3161.  
  3162.       if (full_path)
  3163.         {
  3164.           found_something++;
  3165.           if (type_only)
  3166.         printf ("file\n");
  3167.           else if (path_only)
  3168.         printf ("%s\n", full_path);
  3169.           else
  3170.         printf ("%s is %s\n", command, full_path);
  3171.  
  3172.           free (full_path);
  3173.         }
  3174.       else
  3175.         while (full_path = user_command_matches (command, 1, found_file))
  3176.           {
  3177.         found_something++;
  3178.         found_file++;
  3179.  
  3180.         if (type_only)
  3181.           printf ("file\n");
  3182.         else if (path_only)
  3183.           printf ("%s\n", full_path);
  3184.         else
  3185.           printf ("%s is %s\n", command, full_path);
  3186.  
  3187.         free (full_path);
  3188.           }
  3189.  
  3190.       if (!found_something && !path_only)
  3191.         printf ("%s not found\n", command);
  3192.  
  3193.       goto next_item;
  3194.     }
  3195.  
  3196.       /* We can only get here if "-all" was not specified. */
  3197.       if (!full_path)
  3198.     {
  3199.       if (!type_only && !path_only)
  3200.         printf ("%s not found\n", command);
  3201.     }
  3202.       else
  3203.     {
  3204.       if (type_only)
  3205.         printf ("file\n");
  3206.       else if (path_only)
  3207.         printf ("%s\n", full_path);
  3208.       else
  3209.         {
  3210.           if (hashed)
  3211.         printf ("%s is hashed (%s)\n", command, full_path);
  3212.           else
  3213.         printf ("%s is %s\n", command, full_path);
  3214.         }
  3215.       found_something++;
  3216.       free (full_path);
  3217.     }
  3218.     next_item:
  3219.       list = list->next;
  3220.     }
  3221.   fflush (stdout);
  3222.   return (found_something ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
  3223. }
  3224.  
  3225. /* Declare or change variable attributes. */
  3226. declare_builtin (list)
  3227.      register WORD_LIST *list;
  3228. {
  3229.   return (declare_internal (list, 0));
  3230. }
  3231.  
  3232. local_builtin (list)
  3233.      register WORD_LIST *list;
  3234. {
  3235.   extern int variable_context;
  3236.  
  3237.   if (variable_context)
  3238.     return (declare_internal (list, 1));
  3239.   else
  3240.     {
  3241. #if 1
  3242.       /* Posix D11 says it is an error again. */
  3243.       builtin_error ("Can only be used in a function");
  3244.       return (EXECUTION_FAILURE);
  3245. #else
  3246.       /* P1003.2, draft 9, 3.15.9: "Outside a function, it is equivalent
  3247.      to creating normal variables (as if `local' had been omitted)." */
  3248.       return (declare_internal (list, 0));
  3249. #endif
  3250.     }
  3251. }
  3252.  
  3253. declare_internal (list, no_modifiers)
  3254.      register WORD_LIST *list;
  3255.      int no_modifiers;
  3256. {
  3257.   extern int variable_context, array_needs_making;
  3258.   int flags_on = 0, flags_off = 0;
  3259.   int any_failed = 0;
  3260.  
  3261.   while (list)
  3262.     {
  3263.       register char *t = list->word->word;
  3264.       int *flags;
  3265.  
  3266.       if (*t != '+' && *t != '-')
  3267.     break;
  3268.  
  3269.       if (no_modifiers)
  3270.     {
  3271.       builtin_error ("Modifiers not allowed");
  3272.       return (EXECUTION_FAILURE);
  3273.     }
  3274.  
  3275.       if (*t == '+')
  3276.     flags = &flags_off;
  3277.       else
  3278.     flags = &flags_on;
  3279.  
  3280.       t++;
  3281.  
  3282.       while (*t)
  3283.     {
  3284.       if (*t == 'f')
  3285.         *flags |= att_function, t++;
  3286.       else if (*t == 'x')
  3287.         *flags |= att_exported, t++, array_needs_making = 1;
  3288.       else if (*t == 'r')
  3289.         *flags |= att_readonly, t++;
  3290.       else if (*t == 'i')
  3291.         *flags |= att_integer, t++;
  3292.       else
  3293.         {
  3294.           builtin_error ("unknown option: `-%c'", *t);
  3295.           return (EXECUTION_FAILURE);
  3296.         }
  3297.     }
  3298.  
  3299.       list = list->next;
  3300.     }
  3301.  
  3302.   /* If there are no more arguments left, then we just want to show
  3303.      some variables. */
  3304.   if (!list)
  3305.     {
  3306.       /* If we didn't allow modifiers, then this is the `local' command.
  3307.      Perhaps the flag should be called `local_command' instead of
  3308.      `no_modifiers'.  At any rate, only show local variables defined
  3309.      at this context level. */
  3310.       if (no_modifiers)
  3311.     {
  3312.       register SHELL_VAR **vlist;
  3313.       register int i;
  3314.  
  3315.       vlist = map_over (variable_in_context, shell_variables);
  3316.  
  3317.       if (vlist)
  3318.         {
  3319.           for (i = 0; vlist[i]; i++)
  3320.         print_assignment (vlist[i]);
  3321.  
  3322.           free (vlist);
  3323.         }
  3324.     }
  3325.       else
  3326.     {
  3327.       if (!flags_on)
  3328.         set_builtin ((WORD_LIST *)NULL);
  3329.       else
  3330.         set_or_show_attributes ((WORD_LIST *)NULL, flags_on);
  3331.     }
  3332.  
  3333.       fflush (stdout);
  3334.       return (EXECUTION_SUCCESS);
  3335.     }
  3336.  
  3337.   /* There are arguments left, so we are making variables. */
  3338.   while (list)
  3339.     {
  3340.       char *value, *name = savestring (list->word->word);
  3341.       int offset = assignment (name);
  3342.  
  3343.       if (offset)
  3344.     {
  3345.       name[offset] = '\0';
  3346.       value = name + offset + 1;
  3347.     }
  3348.       else
  3349.     {
  3350.       value = "";
  3351.     }
  3352.  
  3353.       /* If VARIABLE_CONTEXT has a non-zero value, then we are executing
  3354.      inside of a function.    This means we should make local variables,
  3355.      not global ones. */
  3356.  
  3357.       if (variable_context)
  3358.     make_local_variable (name);
  3359.  
  3360.       /* If we are declaring a function, then complain about it in some way.
  3361.      We don't let people make functions by saying `typeset -f foo=bar'. */
  3362.  
  3363.       /* There should be a way, however, to let people look at a particular
  3364.      function definition by saying `typeset -f foo'. */
  3365.  
  3366.       if (flags_on & att_function)
  3367.     {
  3368.       builtin_error ("Can't use `-f' to make functions");
  3369.       return (EXECUTION_FAILURE);
  3370.     }
  3371.       else
  3372.     {
  3373.       SHELL_VAR *bind_variable (), *find_variable (), *v;
  3374.  
  3375.       if ((v = find_variable (name)) == (SHELL_VAR *)NULL)
  3376.         v = bind_variable (name, "");
  3377.  
  3378.       /* We are not allowed to rebind readonly variables that
  3379.          already are readonly unless we are turning off the
  3380.          readonly bit. */
  3381.       if (flags_off & att_readonly)
  3382.         flags_on &= ~att_readonly;
  3383.  
  3384.       if (value && readonly_p (v) && (!(flags_off & att_readonly)))
  3385.         {
  3386.           builtin_error ("%s: readonly variable", name);
  3387.           any_failed++;
  3388.           goto hack_next_variable;
  3389.         }
  3390.  
  3391.       v->attributes |= flags_on;
  3392.       v->attributes &= ~flags_off;
  3393.  
  3394.       if (offset)
  3395.         {
  3396.           free (v->value);
  3397.           if (integer_p (v))
  3398.         {
  3399.           long val, evalexp ();
  3400.           char *itos ();
  3401.  
  3402.           val = evalexp (value);
  3403.           v->value = itos ((int)val);
  3404.         }
  3405.           else
  3406.         v->value = savestring (value);
  3407.         }
  3408.     }
  3409.  
  3410.       stupidly_hack_special_variables (name);
  3411.  
  3412.     hack_next_variable:
  3413.       free (name);
  3414.       list = list->next;
  3415.     }
  3416.   return ((!any_failed) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
  3417. }
  3418.  
  3419.  
  3420. /* **************************************************************** */
  3421. /*                                                                  */
  3422. /*                     UMASK Builtin and Helpers                    */
  3423. /*                                                                  */
  3424. /* **************************************************************** */
  3425.  
  3426. /* Print the umask in a symbolic form.  In the output, a letter is
  3427.    printed if the corresponding bit is clear in the umask. */
  3428. static void
  3429. print_symbolic_umask (um)
  3430.      int um;
  3431. {
  3432.   char ubits[4], gbits[4], obits[4];        /* u=rwx,g=rwx,o=rwx */
  3433.   int i;
  3434.  
  3435.   i = 0;
  3436.   if ((um & S_IRUSR) == 0)
  3437.     ubits[i++] = 'r';
  3438.   if ((um & S_IWUSR) == 0)
  3439.     ubits[i++] = 'w';
  3440.   if ((um & S_IXUSR) == 0)
  3441.     ubits[i++] = 'x';
  3442.   ubits[i] = '\0';
  3443.  
  3444.   i = 0;
  3445.   if ((um & S_IRGRP) == 0)
  3446.     gbits[i++] = 'r';
  3447.   if ((um & S_IWGRP) == 0)
  3448.     gbits[i++] = 'w';
  3449.   if ((um & S_IXGRP) == 0)
  3450.     gbits[i++] = 'x';
  3451.   gbits[i] = '\0';
  3452.  
  3453.   i = 0;
  3454.   if ((um & S_IROTH) == 0)
  3455.     obits[i++] = 'r';
  3456.   if ((um & S_IWOTH) == 0)
  3457.     obits[i++] = 'w';
  3458.   if ((um & S_IXOTH) == 0)
  3459.     obits[i++] = 'x';
  3460.   obits[i] = '\0';
  3461.  
  3462.   printf ("u=%s,g=%s,o=%s\n", ubits, gbits, obits);
  3463. }
  3464.  
  3465. /* Set the umask from a symbolic mode string similar to that accepted
  3466.    by chmod.  If the -S argument is given, then print the umask in a
  3467.    symbolic form. */
  3468. static int
  3469. symbolic_umask (list)
  3470.      WORD_LIST *list;
  3471. {
  3472.   int um, umc, c;
  3473.   int who, op, perm, mask;
  3474.   char *s;
  3475.  
  3476.   um = umask (022);    /* get initial umask */
  3477.   umask (um);        /* and restore it */
  3478.  
  3479.   while (list)
  3480.     {
  3481.       if (strcmp (list->word->word, "-S") == 0)
  3482.     {
  3483.           print_symbolic_umask (um);
  3484.           return (um);
  3485.     }
  3486.       else if (strcmp (list->word->word, "--") == 0)
  3487.     {
  3488.       list = list->next;
  3489.       break;
  3490.     }
  3491.       else if (*(list->word->word) == '-')
  3492.     {
  3493.       bad_option (list->word->word + 1);
  3494.       return (-1);
  3495.     }
  3496.       else
  3497.         break;
  3498.     }
  3499.  
  3500.   if (!list)
  3501.     return (um);
  3502.  
  3503.   /* All work below is done with the complement of the umask -- its
  3504.      more intuitive and easier to deal with.  It is complemented
  3505.      again before being returned. */
  3506.   umc = ~um;
  3507.  
  3508.   s = list->word->word;
  3509.  
  3510.   for (;;)
  3511.     {
  3512.       who = op = perm = mask = 0;
  3513.  
  3514.       /* Parse the `who' portion of the symbolic mode clause. */
  3515.       while (member (*s, "agou"))
  3516.         {
  3517.       switch (c = *s++)
  3518.         {
  3519.           case 'u':
  3520.             who |= S_IRWXU;
  3521.             continue;
  3522.           case 'g':
  3523.             who |= S_IRWXG;
  3524.             continue;
  3525.           case 'o':
  3526.             who |= S_IRWXO;
  3527.             continue;
  3528.           case 'a':
  3529.             who |= S_IRWXU | S_IRWXG | S_IRWXO;
  3530.             continue;
  3531.           default:
  3532.             break;
  3533.         }
  3534.     }
  3535.  
  3536.       /* The operation is now sitting in *s. */
  3537.       op = *s++;
  3538.       switch (op)
  3539.     {
  3540.       case '+':
  3541.       case '-':
  3542.       case '=':
  3543.         break;
  3544.       default:
  3545.         builtin_error ("bad symbolic mode operator: %c", op);
  3546.         return (-1);
  3547.     }
  3548.  
  3549.       /* Parse out the `perm' section of the symbolic mode clause. */
  3550.       while (member (*s, "rwx"))
  3551.     {
  3552.       c = *s++;
  3553.  
  3554.       switch (c)
  3555.         {
  3556.           case 'r':
  3557.         perm |= S_IRUGO;
  3558.         break;
  3559.  
  3560.           case 'w':
  3561.         perm |= S_IWUGO;
  3562.         break;
  3563.  
  3564.           case 'x':
  3565.         perm |= S_IXUGO;
  3566.         break;
  3567.         }
  3568.     }
  3569.  
  3570.       /* Now perform the operation or return an error for a
  3571.      bad permission string. */
  3572.       if (!*s || *s == ',')
  3573.     {
  3574.       if (who)
  3575.         perm &= who;
  3576.  
  3577.       switch (op)
  3578.         {
  3579.           case '+':
  3580.             umc |= perm;
  3581.             break;
  3582.  
  3583.           case '-':
  3584.             umc &= ~perm;
  3585.             break;
  3586.  
  3587.           case '=':
  3588.             umc &= ~who;
  3589.             umc |= perm;
  3590.             break;
  3591.  
  3592.           default:
  3593.               builtin_error ("bad operation character: %c", op);
  3594.               return (-1);
  3595.         }
  3596.  
  3597.       if (!*s)
  3598.         {
  3599.           um = (~umc & 0777);
  3600.           break;
  3601.         }
  3602.       else
  3603.         s++;    /* skip past ',' */
  3604.     }
  3605.       else
  3606.     {
  3607.       builtin_error ("bad character in symbolic mode: %c", *s);
  3608.       return (-1);
  3609.     }
  3610.     }
  3611.   return (um);
  3612. }
  3613.  
  3614. /* Set or display the mask used by the system when creating files. */
  3615. umask_builtin (list)
  3616.      WORD_LIST *list;
  3617. {
  3618.   if (list)
  3619.     {
  3620.       int new_umask;
  3621.  
  3622.       if (digit (*list->word->word))
  3623.     {
  3624.       new_umask = read_octal (list->word->word);
  3625.  
  3626.       /* Note that other shells just let you set the umask to zero
  3627.          by specifying a number out of range.  This is a problem
  3628.          with those shells.  We don't change the umask if the input
  3629.          is lousy. */
  3630.       if (new_umask == -1)
  3631.         {
  3632.           builtin_error ("`%s' is not an octal number from 000 to 777",
  3633.                 list->word->word);
  3634.           return (EXECUTION_FAILURE);
  3635.         }
  3636.     }
  3637.       else
  3638.     {
  3639.       new_umask = symbolic_umask (list);
  3640.       if (new_umask == -1)
  3641.         return (EXECUTION_FAILURE);
  3642.     }
  3643.       umask (new_umask);
  3644.     }
  3645.   else
  3646.     {
  3647.       /* Display the UMASK for this user. */
  3648.       int old_umask = umask (022);
  3649.       umask (old_umask);
  3650.       printf ("%03o\n", old_umask);
  3651.     }
  3652.   fflush (stdout);
  3653.   return (EXECUTION_SUCCESS);
  3654. }
  3655.  
  3656. /* Return the octal number parsed from STRING, or -1 to indicate
  3657.    that the string contained a bad number. */
  3658. int
  3659. read_octal (string)
  3660.      char *string;
  3661. {
  3662.   int result = 0;
  3663.   int digits = 0;
  3664.  
  3665.   while (*string && *string >= '0' && *string < '8')
  3666.     {
  3667.       digits++;
  3668.       result = (result * 8) + *string++ - '0';
  3669.     }
  3670.  
  3671.   if (!digits || result > 0777 || *string)
  3672.     result = -1;
  3673.  
  3674.   return (result);
  3675. }
  3676.  
  3677. unset_builtin (list)
  3678.   WORD_LIST *list;
  3679. {
  3680.   extern char **non_unsettable_vars;
  3681.   int unset_function = 0, unset_variable = 0;
  3682.   int any_failed = 0;
  3683.   char *name;
  3684.  
  3685.   while (list)
  3686.     {
  3687.       name = list->word->word;
  3688.  
  3689.       if (strcmp (name, "-f") == 0)
  3690.     {
  3691.       list = list->next;
  3692.       unset_function++;
  3693.       continue;
  3694.     }
  3695.       if (strcmp (name, "-v") == 0)
  3696.     {
  3697.       list = list->next;
  3698.       unset_variable++;
  3699.       continue;
  3700.     }
  3701.       else if (strcmp (name, "--") == 0)
  3702.     {
  3703.       list = list->next;
  3704.       break;
  3705.     }
  3706.       else if (*name == '-')
  3707.     {
  3708.       bad_option (name);
  3709.       return (EXECUTION_FAILURE);
  3710.     }
  3711.       else
  3712.     break;
  3713.     }
  3714.  
  3715.   if (unset_function && unset_variable)
  3716.     {
  3717.       builtin_error ("cannot simultaneously unset a function and a variable");
  3718.       return (EXECUTION_FAILURE);
  3719.     }
  3720.  
  3721.   while (list)
  3722.     {
  3723.       name = list->word->word;
  3724.  
  3725.       if (!unset_function && find_name_in_list (name, non_unsettable_vars) > -1)
  3726.     {
  3727.       builtin_error ("%s: cannot unset", name);
  3728.       any_failed++;
  3729.     }
  3730.       else
  3731.     {
  3732.       /* Unless the -f option is supplied, the name refers to a
  3733.          variable. */
  3734.       int tem = makunbound (name,
  3735.           unset_function ? shell_functions : shell_variables);
  3736.  
  3737. #if defined (RIGIDLY_POSIX_COMPLIANT)
  3738.       /* This is what Posix.2 draft 11+ says.  ``If neither -f nor -v
  3739.          is specified, the name refers to a variable; if a variable by
  3740.          that name does not exist, a function by that name, if any,
  3741.          shall be unset.'' */
  3742.       if ((tem == -1) && !unset_function && !unset_variable)
  3743.         tem = makunbound (name, shell_functions);
  3744. #endif /* RIGIDLY_POSIX_COMPLIANT */
  3745.  
  3746.       if (tem == -1)
  3747.         any_failed++;
  3748.       else if (!unset_function)
  3749.         stupidly_hack_special_variables (name);
  3750.     }
  3751.       list = list->next;
  3752.     }
  3753.  
  3754.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  3755. }
  3756.  
  3757. /* Run the command mentioned in list directly, without going through the
  3758.    normal alias/function/builtin/filename lookup process. */
  3759. builtin_builtin (list)
  3760.      WORD_LIST *list;
  3761. {
  3762.   extern Function *find_shell_builtin ();
  3763.   Function *function;
  3764.   register char *command;
  3765.  
  3766.   if (!list)
  3767.     return (EXECUTION_SUCCESS);
  3768.  
  3769.   command = (list->word->word);
  3770.   function = find_shell_builtin (command);
  3771.  
  3772.   if (!function)
  3773.     {
  3774.       builtin_error ("%s: Not a shell builtin!", command);
  3775.       return (EXECUTION_FAILURE);
  3776.     }
  3777.   else
  3778.     {
  3779.       this_command_name = command;
  3780.       list = list->next;
  3781.       return ((*function) (list));
  3782.     }
  3783. }
  3784.  
  3785. decrement_variable (var)
  3786.      int *var;
  3787. {
  3788.   *var = *var - 1;
  3789. }
  3790.  
  3791. /* Run the commands mentioned in LIST without paying attention to shell
  3792.    functions. */
  3793. int ignore_function_references = 0;
  3794.  
  3795. command_builtin (list)
  3796.      WORD_LIST *list;
  3797. {
  3798.   int result;
  3799.  
  3800.   begin_unwind_frame ("command_builtin");
  3801.   add_unwind_protect (decrement_variable, &ignore_function_references);
  3802.   ignore_function_references++;
  3803.  
  3804.   /* We don't want this to be reparsed (consider command echo 'foo &'), so
  3805.      just make a simple_command structure and call execute_command with it. */
  3806.   if (!list)
  3807.     result = EXECUTION_SUCCESS;
  3808.   else
  3809.     {
  3810.       COMMAND *command;
  3811.       extern COMMAND *make_bare_simple_command ();
  3812.       extern WORD_LIST *copy_word_list ();
  3813.  
  3814.       command = make_bare_simple_command ();
  3815.       command->value.Simple->words = (WORD_LIST *)copy_word_list (list);
  3816.       command->value.Simple->redirects = (REDIRECT *)NULL;
  3817.       result = execute_command (command);
  3818.       dispose_command (command);
  3819.     }
  3820.  
  3821.   run_unwind_frame ("command_builtin");
  3822.  
  3823.   return (result);
  3824. }
  3825.  
  3826. char tdir[MAXPATHLEN];
  3827. /* Return a pretty pathname.  If the first part of the pathname is
  3828.    the same as $HOME, then replace that with `~'.  */
  3829. char *
  3830. polite_directory_format (name)
  3831.      char *name;
  3832. {
  3833.   char *home = get_string_value ("HOME");
  3834.   int l = home ? strlen (home) : 0;
  3835.  
  3836.   if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
  3837.     {
  3838.       strcpy (tdir + 1, name + l);
  3839.       tdir[0] = '~';
  3840.       return (tdir);
  3841.     }
  3842.   else
  3843.     return (name);
  3844. }
  3845.  
  3846. #ifdef PUSHD_AND_POPD
  3847.  
  3848. /* Some useful commands whose behaviour has been observed in csh. */
  3849.  
  3850. /* The list of remembered directories. */
  3851. char **pushd_directory_list = (char **)NULL;
  3852.  
  3853. /* Number of existing slots in this list. */
  3854. int directory_list_size = 0;
  3855.  
  3856. /* Offset to the end of the list. */
  3857. int directory_list_offset = 0;
  3858.  
  3859. pushd_builtin (list)
  3860.      WORD_LIST *list;
  3861. {
  3862.   char *temp, *current_directory, *get_working_directory ();
  3863.   int j = directory_list_offset - 1;
  3864.   char direction = '+';
  3865.  
  3866.   /* If there is no argument list then switch current and
  3867.      top of list. */
  3868.   if (!list)
  3869.     {
  3870.       if (!directory_list_offset)
  3871.     {
  3872.       builtin_error ("No other directory");
  3873.       return (EXECUTION_FAILURE);
  3874.     }
  3875.  
  3876.       current_directory = get_working_directory ("pushd");
  3877.       if (!current_directory)
  3878.     return (EXECUTION_FAILURE);
  3879.  
  3880.       temp = pushd_directory_list[j];
  3881.       pushd_directory_list[j] = current_directory;
  3882.       goto change_to_temp;
  3883.     }
  3884.   else
  3885.     {
  3886.       direction = *(list->word->word);
  3887.       if (direction == '+' || direction == '-')
  3888.     {
  3889.       int num;
  3890.       if (1 == sscanf (&(list->word->word)[1], "%d", &num))
  3891.         {
  3892.           if (direction == '-')
  3893.         num = directory_list_offset - num;
  3894.  
  3895.           if (num > directory_list_offset || num < 0)
  3896.         {
  3897.           if (!directory_list_offset)
  3898.             builtin_error ("Directory stack empty");
  3899.           else
  3900.             builtin_error ("Stack contains only %d directories",
  3901.                     directory_list_offset + 1);
  3902.           return (EXECUTION_FAILURE);
  3903.         }
  3904.           else
  3905.         {
  3906.           /* Rotate the stack num times.  Remember, the
  3907.              current directory acts like it is part of the
  3908.              stack. */
  3909.           temp = get_working_directory ("pushd");
  3910.  
  3911.           if (!num)
  3912.             goto change_to_temp;
  3913.  
  3914.           do
  3915.             {
  3916.               char *top =
  3917.             pushd_directory_list[directory_list_offset - 1];
  3918.  
  3919.               for (j = directory_list_offset - 2; j > -1; j--)
  3920.             pushd_directory_list[j + 1] = pushd_directory_list[j];
  3921.  
  3922.               pushd_directory_list[j + 1] = temp;
  3923.  
  3924.               temp = top;
  3925.               num--;
  3926.             }
  3927.           while (num);
  3928.  
  3929.           temp = savestring (temp);
  3930.         change_to_temp:
  3931.           {
  3932.             int tt = EXECUTION_FAILURE;
  3933.  
  3934.             if (temp)
  3935.               {
  3936.             tt = cd_to_string (temp);
  3937.             free (temp);
  3938.               }
  3939.  
  3940.             if ((tt == EXECUTION_SUCCESS) &&
  3941.             (!find_variable ("pushd_silent")))
  3942.               dirs_builtin ((WORD_LIST *)NULL);
  3943.  
  3944.             return (tt);
  3945.           }
  3946.         }
  3947.         }
  3948.     }
  3949.  
  3950.       /* Change to the directory in list->word->word.  Save the current
  3951.      directory on the top of the stack. */
  3952.       current_directory = get_working_directory ("pushd");
  3953.       if (!current_directory)
  3954.     return (EXECUTION_FAILURE);
  3955.  
  3956.       if (cd_builtin (list) == EXECUTION_SUCCESS)
  3957.     {
  3958.       if (directory_list_offset == directory_list_size)
  3959.         {
  3960.           pushd_directory_list = (char **)
  3961.         xrealloc (pushd_directory_list,
  3962.               (directory_list_size += 10) * sizeof (char *));
  3963.         }
  3964.       pushd_directory_list[directory_list_offset++] = current_directory;
  3965.  
  3966.       if (!find_variable ("pushd_silent"))
  3967.         dirs_builtin ((WORD_LIST *)NULL);
  3968.  
  3969.       return (EXECUTION_SUCCESS);
  3970.     }
  3971.       else
  3972.     {
  3973.       free (current_directory);
  3974.       return (EXECUTION_FAILURE);
  3975.     }
  3976.     }
  3977. }
  3978.  
  3979. /* Print the current list of directories on the directory stack. */
  3980. dirs_builtin (list)
  3981.      WORD_LIST *list;
  3982. {
  3983.   register int i, format = 0;
  3984.   char *temp, *polite_directory_format (), *get_working_directory ();
  3985.  
  3986.   /* Maybe do long form? */
  3987.   if (list)
  3988.     {
  3989.       if (strcmp (list->word->word, "-l") == 0)
  3990.     format++;
  3991.       else if (!format || list->next)
  3992.     {
  3993.       bad_option (list->word->word);
  3994.       return (EXECUTION_FAILURE);
  3995.     }
  3996.     }
  3997.  
  3998.   /* The first directory printed is always the current working directory. */
  3999.   temp = get_working_directory ("dirs");
  4000.   if (!temp)
  4001.     temp = savestring ("<no directory>");
  4002.   printf ("%s ", format ? temp : polite_directory_format (temp));
  4003.   free (temp);
  4004.  
  4005.   /* Now print any directories in the array. */
  4006.   for (i = (directory_list_offset - 1); i > -1; i--)
  4007.     printf ("%s ", format ? pushd_directory_list[i] :
  4008.         polite_directory_format (pushd_directory_list[i]));
  4009.  
  4010.   printf ("\n");
  4011.   fflush (stdout);
  4012.   return (EXECUTION_SUCCESS);
  4013. }
  4014.  
  4015. /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
  4016.    so if the result is EXECUTION_FAILURE then an error message has already
  4017.    been printed. */
  4018. cd_to_string (name)
  4019.      char *name;
  4020. {
  4021.   extern WORD_LIST *make_word_list ();
  4022.   WORD_LIST *tlist = make_word_list (make_word (name), NULL);
  4023.   int result = (cd_builtin (tlist));
  4024.   dispose_words (tlist);
  4025.   return (result);
  4026. }
  4027.  
  4028. /* Pop the directory stack, and then change to the new top of the stack.
  4029.    If LIST is non-null it should consist of a word +N or -N, which says
  4030.    what element to delete from the stack.  The default is the top one. */
  4031. popd_builtin (list)
  4032.      WORD_LIST *list;
  4033. {
  4034.   register int i;
  4035.   int which = 0;
  4036.   char direction = '+';
  4037.  
  4038.   if (list)
  4039.     {
  4040.       direction = *(list->word->word);
  4041.  
  4042.       if ((direction != '+' && direction != '-') ||
  4043.       (1 != sscanf (&((list->word->word)[1]), "%d", &which)))
  4044.     {
  4045.       builtin_error ("bad arg `%s'", list->word->word);
  4046.       return (EXECUTION_FAILURE);
  4047.     }
  4048.     }
  4049.  
  4050.   if (which > directory_list_offset || (!directory_list_offset && !which))
  4051.     {
  4052.       if (!directory_list_offset)
  4053.     builtin_error ("Directory stack empty");
  4054.       else
  4055.     builtin_error ("Stack contains only %d directories",
  4056.             directory_list_offset + 1);
  4057.       return (EXECUTION_FAILURE);
  4058.     }
  4059.  
  4060.   /* Handle case of no specification, or top of stack specification. */
  4061.   if ((direction == '+' && which == 0) ||
  4062.       (direction == '-' && which == directory_list_offset))
  4063.     {
  4064.       i = cd_to_string (pushd_directory_list[directory_list_offset - 1]);
  4065.       if (i != EXECUTION_SUCCESS)
  4066.     return (i);
  4067.       free (pushd_directory_list[--directory_list_offset]);
  4068.     }
  4069.   else
  4070.     {
  4071.       /* Since an offset other than the top directory was specified,
  4072.      remove that directory from the list and shift the remainder
  4073.      of the list into place. */
  4074.  
  4075.       if (direction == '+')
  4076.     i = directory_list_offset - which;
  4077.       else
  4078.     i = which;
  4079.  
  4080.       free (pushd_directory_list[i]);
  4081.       directory_list_offset--;
  4082.  
  4083.       /* Shift the remainder of the list into place. */
  4084.       for (; i < directory_list_offset; i++)
  4085.     pushd_directory_list[i] = pushd_directory_list[i + 1];
  4086.     }
  4087.  
  4088.   if (!find_variable ("pushd_silent"))
  4089.     dirs_builtin ((WORD_LIST *)NULL);
  4090.  
  4091.   return (EXECUTION_SUCCESS);
  4092. }
  4093.  
  4094. #endif    /* PUSHD_AND_POPD */
  4095.  
  4096. #ifdef ALIAS
  4097.  
  4098. /* Hack the alias command in a Korn shell way. */
  4099. alias_builtin (list)
  4100.      WORD_LIST *list;
  4101. {
  4102.   int any_failed = 0;
  4103.  
  4104.   if (!list)
  4105.     {
  4106.       register int i;
  4107.  
  4108.       if (!aliases)
  4109.     return (EXECUTION_FAILURE);
  4110.  
  4111.       for (i = 0; aliases[i]; i++)
  4112.     print_alias (aliases[i]);
  4113.     }
  4114.   else
  4115.     {
  4116.       while (list)
  4117.     {
  4118.       register char *value, *name = list->word->word;
  4119.       register int offset;
  4120.  
  4121.       for (offset = 0; name[offset] && name[offset] != '='; offset++);
  4122.  
  4123.       if (offset && name[offset] == '=')
  4124.         {
  4125.           name[offset] = '\0';
  4126.           value = name + offset + 1;
  4127.  
  4128.           add_alias (name, value);
  4129.         }
  4130.       else
  4131.         {
  4132.           ASSOC *t = find_alias (name);
  4133.           if (t)
  4134.         print_alias (t);
  4135.           else
  4136.         {
  4137.           if (interactive)
  4138.             builtin_error ("`%s' not found", name);
  4139.           any_failed++;
  4140.         }
  4141.         }
  4142.       list = list->next;
  4143.     }
  4144.     }
  4145.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  4146. }
  4147.  
  4148. /* Remove aliases named in LIST from the aliases database. */
  4149. unalias_builtin (list)
  4150.      register WORD_LIST *list;
  4151. {
  4152.   register ASSOC *alias;
  4153.   int any_failed = 0;
  4154.  
  4155.   while (list)
  4156.     {
  4157.       alias = find_alias (list->word->word);
  4158.  
  4159.       if (alias)
  4160.     remove_alias (alias->name);
  4161.       else
  4162.     {
  4163.       if (interactive)
  4164.         builtin_error ("`%s' not an alias", list->word->word);
  4165.  
  4166.       any_failed++;
  4167.     }
  4168.  
  4169.       list = list->next;
  4170.     }
  4171.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  4172. }
  4173.  
  4174. /* Output ALIAS in such a way as to allow it to be read back in. */
  4175. print_alias (alias)
  4176.      ASSOC *alias;
  4177. {
  4178.   register int i;
  4179.  
  4180.   for (i = 0; alias->value[i] && !whitespace (alias->value[i]); i++);
  4181.  
  4182.   if (alias->value[i])
  4183.     printf ("alias %s=\"%s\"\n", alias->name, alias->value);
  4184.   else
  4185.     printf ("alias %s=%s\n", alias->name, alias->value);
  4186.  
  4187.   fflush (stdout);
  4188. }
  4189.  
  4190. #endif    /* ALIAS */
  4191.  
  4192. /* Wait for the pid in LIST to stop or die.  If no arguments are given, then
  4193.    wait for all of the active background processes of the shell and return
  4194.    0.  If a list of pids or job specs are given, return the exit status of
  4195.    the last one waited for. */
  4196. wait_builtin (list)
  4197.      WORD_LIST *list;
  4198. {
  4199.   extern void wait_for_background_pids ();
  4200.   extern int wait_for_single_pid ();
  4201.   extern int job_control;
  4202.   int status = EXECUTION_SUCCESS;
  4203.  
  4204.   /* We support jobs or pids.
  4205.      wait <pid-or-job> [pid-or-job ...] */
  4206.  
  4207.   /* But wait without any arguments means to wait for all of the shell's
  4208.      currently active background processes. */
  4209.   if (!list)
  4210.     {
  4211.       wait_for_background_pids ();
  4212.       return (EXECUTION_SUCCESS);
  4213.     }
  4214.  
  4215.   while (list)
  4216.     {
  4217.       pid_t pid;
  4218.       if (digit (*(list->word->word)))
  4219.     {
  4220.       if (sscanf (list->word->word, "%d", &pid) == 1)
  4221.         status = wait_for_single_pid (pid);
  4222.       else
  4223.         {
  4224.           builtin_error ("`%s' is not a pid or job spec", list->word->word);
  4225.           return (EXECUTION_FAILURE);
  4226.         }
  4227.     }
  4228. #if defined (JOB_CONTROL)
  4229.       else if (job_control)
  4230.     /* Must be a job spec.    Check it out. */
  4231.     {
  4232.       int job;
  4233.       sigset_t set, oset;
  4234.  
  4235.       BLOCK_CHILD (set, oset);
  4236.       job = get_job_spec (list);
  4237.  
  4238.       if (job < 0 || job >= job_slots || !jobs[job])
  4239.         {
  4240.           if (job != DUP_JOB)
  4241.         builtin_error ("No such job %s", list->word->word);
  4242.           UNBLOCK_CHILD (oset);
  4243.           status = EXECUTION_FAILURE;
  4244.           list = list->next;
  4245.           continue;
  4246.         }
  4247.  
  4248.       /* Job spec used.  Wait for the last pid in the pipeline. */
  4249.  
  4250.       UNBLOCK_CHILD (oset);
  4251.  
  4252.       status = wait_for_job (job);
  4253.     }
  4254. #endif /* JOB_CONTROL */
  4255.       else
  4256.     {
  4257.       builtin_error ("%s is not a pid or legal job spec",
  4258.              list->word->word);
  4259.       status = EXECUTION_FAILURE;
  4260.     }
  4261.       list = list->next;
  4262.     }
  4263.   return (status);
  4264. }
  4265.  
  4266. #if defined (JOB_CONTROL)
  4267. /* **************************************************************** */
  4268. /*                                    */
  4269. /*            Job Control!                    */
  4270. /*                                    */
  4271. /* **************************************************************** */
  4272.  
  4273. extern int job_control;
  4274.  
  4275. #define NORMAL        0
  4276. #define LONG_FORM    1
  4277. #define PID_ONLY    2
  4278. #define CHANGED_ONLY    3
  4279.  
  4280. /* The `jobs' command.    Prints outs a list of active jobs.  If the
  4281.    argument `-l' is given, then the process id's are printed also.
  4282.    If the argument `-p' is given, print the process group leader's
  4283.    pid only. */
  4284. jobs_builtin (list)
  4285.      WORD_LIST *list;
  4286. {
  4287.   int form = NORMAL;
  4288.  
  4289.   if (!job_control)
  4290.     return (EXECUTION_SUCCESS);
  4291.  
  4292.   while (list && (*list->word->word == '-'))
  4293.     {
  4294.       if (strcmp (list->word->word, "-l") == 0)
  4295.     form = LONG_FORM;
  4296.       else if (strcmp (list->word->word, "-p") == 0)
  4297.     form = PID_ONLY;
  4298.       else
  4299.     {
  4300.       bad_option (list->word->word);
  4301.       return (EXECUTION_FAILURE);
  4302.     }
  4303.       list = list->next;
  4304.     }
  4305.  
  4306.   if (!list)
  4307.     {
  4308.       list_jobs (form);
  4309.       return (EXECUTION_SUCCESS);
  4310.     }
  4311.  
  4312.   while (list)
  4313.     {
  4314.       int job;
  4315.       sigset_t set, oset;
  4316.  
  4317.       BLOCK_CHILD (set, oset);
  4318.       job = get_job_spec (list);
  4319.  
  4320.       if ((job == NO_JOB) || !jobs || !jobs[job])
  4321.     builtin_error ("No such job %s", list->word->word);
  4322.       else if (job != DUP_JOB)
  4323.     list_one_job ((JOB *)NULL, form, 0, job);
  4324.  
  4325.       UNBLOCK_CHILD (oset);
  4326.       list = list->next;
  4327.     }
  4328.     return (EXECUTION_SUCCESS);
  4329. }
  4330.  
  4331. /* Suspend the current shell.  Not hard to do. */
  4332.  
  4333. static SigHandler *old_cont, *old_tstp;
  4334.  
  4335. /* Continue handler. */
  4336. sighandler
  4337. suspend_continue ()
  4338. {
  4339.   signal (SIGCONT, old_cont);
  4340.   signal (SIGTSTP, old_tstp);
  4341. }
  4342.  
  4343. /* Suspending the shell.  If -f is the arg, then do the suspend
  4344.    no matter what.  Otherwise, complain if a login shell. */
  4345. suspend_builtin (list)
  4346.      WORD_LIST *list;
  4347. {
  4348.   if (!job_control)
  4349.     {
  4350.       builtin_error ("Cannot suspend a shell without job control");
  4351.       return (EXECUTION_FAILURE);
  4352.     }
  4353.  
  4354.   if (list)
  4355.     if (strcmp (list->word->word, "-f") == 0)
  4356.       goto do_suspend;
  4357.  
  4358.   no_args (list);
  4359.  
  4360.   if (login_shell)
  4361.     {
  4362.       builtin_error ("Can't suspend a login shell");
  4363.       return (EXECUTION_FAILURE);    /* XXX was longjmp (top_level, ...) */
  4364.     }
  4365.  
  4366. do_suspend:
  4367.   old_cont = (SigHandler *)signal (SIGCONT, suspend_continue);
  4368.   old_tstp = (SigHandler *)signal (SIGTSTP, SIG_DFL);
  4369.   killpg (shell_pgrp, SIGTSTP);
  4370.   return (EXECUTION_SUCCESS);
  4371. }
  4372.  
  4373. /* How to bring a job into the foreground. */
  4374. fg_builtin (list)
  4375.      WORD_LIST *list;
  4376. {
  4377.   int fg_bit = 1;
  4378.   register WORD_LIST *t = list;
  4379.  
  4380.   if (!job_control)
  4381.     return (EXECUTION_SUCCESS);
  4382.  
  4383.   /* If the last arg on the line is '&', then start this job in the
  4384.      background.  Else, fg the job. */
  4385.  
  4386.   while (t && t->next)
  4387.     t = t->next;
  4388.  
  4389.   if (t && strcmp (t->word->word, "&") == 0)
  4390.     fg_bit = 0;
  4391.  
  4392.   return (fg_bg (list, fg_bit));
  4393. }
  4394.  
  4395. /* How to put a job into the background. */
  4396. bg_builtin (list)
  4397.      WORD_LIST *list;
  4398. {
  4399.   if (!job_control)
  4400.     return (EXECUTION_SUCCESS);
  4401.  
  4402.   return (fg_bg (list, 0));
  4403. }
  4404.  
  4405. /* How to put a job into the foreground/background. */
  4406. fg_bg (list, foreground)
  4407.      WORD_LIST *list;
  4408.      int foreground;
  4409. {
  4410.   extern char *this_command_name;
  4411.   sigset_t set, oset;
  4412.   int job;
  4413.  
  4414.   BLOCK_CHILD (set, oset);
  4415.   job = get_job_spec (list);
  4416.  
  4417.   if (job < 0 || job >= job_slots || !jobs[job])
  4418.     {
  4419.       if (job != DUP_JOB)
  4420.     builtin_error ("No such job %s", list ? list->word->word : "");
  4421.  
  4422.       goto failure;
  4423.     }
  4424.  
  4425.   /* Or if jobs[job]->pgrp == shell_pgrp. */
  4426.   if (jobs[job]->job_control == 0)
  4427.     {
  4428.       builtin_error ("job %%%d started without job control", job + 1);
  4429.       goto failure;
  4430.     }
  4431.  
  4432.   if (start_job (job, foreground))
  4433.     {
  4434.     win:
  4435.       UNBLOCK_CHILD (oset);
  4436.       return (EXECUTION_SUCCESS);
  4437.     }
  4438.   else
  4439.     {
  4440.     failure:
  4441.       UNBLOCK_CHILD (oset);
  4442.       return (EXECUTION_FAILURE);
  4443.     }
  4444. }
  4445.  
  4446. /* Return the job spec found in LIST. */
  4447. get_job_spec (list)
  4448.      WORD_LIST *list;
  4449. {
  4450.   register char *word;
  4451.   int job = NO_JOB;
  4452.   int substring = 0;
  4453.  
  4454.   if (!list)
  4455.     return (current_job);
  4456.  
  4457.   word = list->word->word;
  4458.  
  4459.   if (!*word)
  4460.     return (current_job);
  4461.  
  4462.   if (*word == '%')
  4463.     word++;
  4464.  
  4465.   if (digit (*word) && (sscanf (word, "%d", &job) == 1))
  4466.     return (job - 1);
  4467.  
  4468.   switch (*word)
  4469.     {
  4470.     case 0:
  4471.     case '%':
  4472.     case '+':
  4473.       return (current_job);
  4474.  
  4475.     case '-':
  4476.       return (previous_job);
  4477.  
  4478.     case '?':            /* Substring search requested. */
  4479.       substring++;
  4480.       word++;
  4481.       goto find_string;
  4482.  
  4483.     default:
  4484.     find_string:
  4485.       {
  4486.     register int i, wl = strlen (word);
  4487.     for (i = 0; i < job_slots; i++)
  4488.       {
  4489.         if (jobs[i])
  4490.           {
  4491.         register PROCESS *p = jobs[i]->pipe;
  4492.         extern char *strindex ();
  4493.         do
  4494.           {
  4495.             if ((substring && strindex (p->command, word)) ||
  4496.             (strncmp (p->command, word, wl) == 0))
  4497.               if (job != NO_JOB)
  4498.             {
  4499.               builtin_error ("ambigious job spec: %s", word);
  4500.               return (DUP_JOB);
  4501.             }
  4502.               else
  4503.             job = i;
  4504.  
  4505.             p = p->next;
  4506.           }
  4507.         while (p != jobs[i]->pipe);
  4508.           }
  4509.       }
  4510.     return (job);
  4511.       }
  4512.     }
  4513. }
  4514.  
  4515. #ifndef CONTINUE_AFTER_KILL_ERROR
  4516. #define CONTINUE_OR_FAIL return (EXECUTION_FAILURE)
  4517. #else
  4518. #define CONTINUE_OR_FAIL goto continue_killing
  4519. #endif
  4520.  
  4521. /* Here is the kill builtin.  We only have it so that people can type
  4522.    kill -KILL %1?  No, if you fill up the process table this way you
  4523.    can still kill some. */
  4524. kill_builtin (list)
  4525.      WORD_LIST *list;
  4526. {
  4527.   int signal = SIGTERM;
  4528.   int any_failed = 0, listing = 0;
  4529.   char *sigspec;
  4530.   pid_t pid;
  4531.  
  4532.   if (!list)
  4533.     return (EXECUTION_SUCCESS);
  4534.  
  4535.   /* Option processing. */
  4536.   while (list)
  4537.     {
  4538.       if (strcmp (list->word->word, "-l") == 0)
  4539.     {
  4540.       listing++;
  4541.       list = list->next;
  4542.     }
  4543.       else if (strcmp (list->word->word, "-s") == 0)
  4544.     {
  4545.       list = list->next;
  4546.       if (list)
  4547.         {
  4548.           sigspec = list->word->word;
  4549.           if (strcmp (sigspec, "0") == 0)
  4550.             signal = 0;
  4551.           else
  4552.             signal = decode_signal (sigspec);
  4553.           list = list->next;
  4554.         }
  4555.       else
  4556.         {
  4557.           builtin_error ("-s requires an argument");
  4558.           return (EXECUTION_FAILURE);
  4559.         }
  4560.     }
  4561.       else if (strcmp (list->word->word, "--") == 0)
  4562.     {
  4563.       list = list->next;
  4564.       break;
  4565.     }
  4566.       else if (*(list->word->word) == '-')
  4567.     {
  4568.       sigspec = &(list->word->word)[1];
  4569.       signal = decode_signal (sigspec);
  4570.       list = list->next;
  4571.     }
  4572.       else
  4573.     break;
  4574.     }
  4575.  
  4576.   if (listing)
  4577.     {
  4578.       if (!list)
  4579.     {
  4580.       register int i, column = 0;
  4581.       for (i = 1; i < NSIG; i++)
  4582.         {
  4583.           char *name = signal_name (i);
  4584.           if ((strncmp (name, "SIGJUNK", 7) == 0) ||
  4585.           (strncmp (name, "Unknown", 7) == 0))
  4586.         continue;
  4587.  
  4588.           printf ("%2d) %s", i, name);
  4589.  
  4590.           if (++column < 4)
  4591.         printf ("\t");
  4592.           else
  4593.         {
  4594.           printf ("\n");
  4595.           column = 0;
  4596.         }
  4597.         }
  4598.       if (column != 0)
  4599.         printf ("\n");
  4600.     }
  4601.       else    /* listing individual signal names */
  4602.     {
  4603.       while (list)
  4604.         {
  4605.           int signum;
  4606.           char *name;
  4607.  
  4608.           if ((sscanf (list->word->word, "%d", &signum) != 1) ||
  4609.           (signum <= 0))
  4610.         {
  4611.         list_error:
  4612.           builtin_error ("bad signal number: %s", list->word->word);
  4613.           list = list->next;
  4614.           continue;
  4615.         }
  4616.  
  4617.           /* This is specified by Posix.2 so that exit statuses can be
  4618.              mapped into signal numbers. */
  4619.           if (signum > 128)
  4620.             signum -= 128;
  4621.  
  4622.           if (signum > NSIG)
  4623.             goto list_error;
  4624.  
  4625.           name = signal_name (signum);
  4626.           if ((strncmp (name, "SIGJUNK", 7) == 0) ||
  4627.           (strncmp (name, "Unknown", 7) == 0))
  4628.         {
  4629.           list = list->next;
  4630.           continue;
  4631.         }
  4632.           printf ("%s\n", name);
  4633.           list = list->next;
  4634.         }
  4635.     }
  4636.       return (EXECUTION_SUCCESS);
  4637.     }
  4638.  
  4639.   /* OK, we are killing processes. */
  4640.   if (signal == NO_SIG)
  4641.     {
  4642.       builtin_error ("bad signal spec `%s'", sigspec);
  4643.       return (EXECUTION_FAILURE);
  4644.     }
  4645.  
  4646.   while (list)
  4647.     {
  4648.       if (digit (*(list->word->word)))
  4649.     {
  4650.       if (sscanf (list->word->word, "%d", (int *)&pid) == 1)
  4651.         {
  4652.           if (kill_pid (pid, signal, 0) < 0)
  4653.         goto signal_error;
  4654.         }
  4655.       else
  4656.         {
  4657.           builtin_error ("No such pid %d", (int)pid);
  4658.           CONTINUE_OR_FAIL;
  4659.         }
  4660.     }
  4661.       else if (job_control)    /* can't kill jobs if not using job control */
  4662.     {            /* Must be a job spec.  Check it out. */
  4663.       int job;
  4664.       sigset_t set, oset;
  4665.  
  4666.       BLOCK_CHILD (set, oset);
  4667.       job = get_job_spec (list);
  4668.  
  4669.       if (job < 0 || job >= job_slots || !jobs[job])
  4670.         {
  4671.           if (job != DUP_JOB)
  4672.         builtin_error ("No such job %s", list->word->word);
  4673.           UNBLOCK_CHILD (oset);
  4674.           CONTINUE_OR_FAIL;
  4675.         }
  4676.  
  4677.       /* Job spec used.  Kill the process group. If the job was started
  4678.          without job control, then its pgrp == shell_pgrp, so we have
  4679.          to be careful.  We take the pid of the first job in the pipeline
  4680.          in that case. */
  4681.       if (jobs[job]->job_control)
  4682.         pid = jobs[job]->pgrp;
  4683.       else
  4684.         pid = jobs[job]->pipe->pid;
  4685.  
  4686.       UNBLOCK_CHILD (oset);
  4687.  
  4688.       if (kill_pid (pid, signal, 1) < 0)
  4689.         {
  4690.         signal_error:
  4691.           if (errno == EPERM)
  4692.         builtin_error ("(%d) - Not owner", (int)pid);
  4693.           else if (errno == ESRCH)
  4694.         builtin_error ("(%d) - No such pid", (int)pid);
  4695.           else
  4696.         builtin_error ("Invalid signal %d", signal);
  4697.           any_failed++;
  4698.           CONTINUE_OR_FAIL;
  4699.         }
  4700.     }
  4701.       else
  4702.     {
  4703.       builtin_error ("bad process specification `%s'", list->word->word);
  4704.       CONTINUE_OR_FAIL;
  4705.     }
  4706.     continue_killing:
  4707.       list = list->next;
  4708.     }
  4709.   return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
  4710. }
  4711.  
  4712. /* #define DETACH */
  4713.  
  4714. #if defined (_POSIX_VERSION)
  4715. #undef DETACH
  4716. #endif
  4717.  
  4718. #if defined (DETACH)
  4719. detach_builtin (list)
  4720.      WORD_LIST *list;
  4721. {
  4722.   int job = NO_JOB;
  4723.   JOB_STATE job_state;
  4724.   PROCESS *process, *pipeline;
  4725.   sigset_t set, oset;
  4726.  
  4727.   if (!job_control)
  4728.     return (EXECUTION_SUCCESS);
  4729.  
  4730.   BLOCK_CHILD (set, oset);
  4731.  
  4732.   job = get_job_spec (list);
  4733.   job_state = JOBSTATE(job);
  4734.  
  4735.   killpg (jobs[job]->pgrp, SIGSTOP);
  4736.   while (jobs[job] && JOBSTATE (job) == JRUNNING)
  4737.     sigpause (0);
  4738.  
  4739.   /* Make the process group of the pipeline the same as init's.     */
  4740.   process = pipeline = jobs[job]->pipe;
  4741.   do
  4742.     {
  4743.       setpgrp (process->pid, 1);
  4744.       process = process -> next;
  4745.     }
  4746.   while (process != pipeline);
  4747.  
  4748.   /* Return the pipeline to the state it was in before we stopped it. */
  4749.   if (job_state == JRUNNING)
  4750.     {
  4751.       process = pipeline;
  4752.       do
  4753.     {
  4754.       kill (process->pid, SIGCONT);
  4755.       process = process -> next;
  4756.     }
  4757.       while (process != pipeline);
  4758.     }
  4759.  
  4760.   /* Remove the job from the job list. */
  4761.   delete_job (job);
  4762.  
  4763.   UNBLOCK_CHILD (oset);
  4764. }
  4765. #endif /* DETACH */
  4766. #endif    /* JOB_CONTROL */
  4767.  
  4768. /* Arithmetic LET function. */
  4769. let_builtin (list)
  4770.      WORD_LIST *list;
  4771. {
  4772.   long ret = 0L;
  4773.   extern long evalexp ();
  4774.  
  4775.   if (!list)
  4776.     {
  4777.       builtin_error ("argument (expression) expected");
  4778.       return (EXECUTION_FAILURE);
  4779.     }
  4780.  
  4781.   while (list)
  4782.     {
  4783.       ret = evalexp (list->word->word);
  4784.       list = list->next;
  4785.     }
  4786.  
  4787.   if (ret == 0L)
  4788.     return (EXECUTION_FAILURE);
  4789.   else
  4790.     return (EXECUTION_SUCCESS);
  4791. }
  4792.  
  4793. /* This is a lot like report_error (), but it is for shell builtins instead
  4794.    of shell control structures, and it won't ever exit the shell. */
  4795. #if defined (HAVE_VPRINTF)
  4796. /* VARARGS */
  4797. builtin_error (va_alist)
  4798.      va_dcl
  4799. {
  4800.   extern char *this_command_name;
  4801.   char *format;
  4802.   va_list args;
  4803.  
  4804.   if (this_command_name && *this_command_name)
  4805.     fprintf (stderr, "%s: ", this_command_name);
  4806.  
  4807.   va_start (args);
  4808.   format = va_arg (args, char *);
  4809.   vfprintf (stderr, format, args);
  4810.   va_end (args);
  4811.   fprintf (stderr, "\n");
  4812. }
  4813.  
  4814. #else /* Without VARARGS. */
  4815.  
  4816. builtin_error (format, arg1, arg2, arg3, arg4, arg5)
  4817.      char *format, *arg1, *arg2, *arg3, *arg4, *arg5;
  4818. {
  4819.   extern char *this_command_name;
  4820.  
  4821.   if (this_command_name && *this_command_name)
  4822.     fprintf (stderr, "%s: ", this_command_name);
  4823.  
  4824.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  4825.   fprintf (stderr, "\n");
  4826.   fflush (stderr);
  4827. }
  4828. #endif /* HAVE_VPRINTF */
  4829.  
  4830.